Skip to content

Commit

Permalink
refactor tool use to use a tea-like architecture as well
Browse files Browse the repository at this point in the history
  • Loading branch information
dlants committed Dec 15, 2024
1 parent 97b8518 commit 5100c98
Show file tree
Hide file tree
Showing 12 changed files with 568 additions and 458 deletions.
4 changes: 2 additions & 2 deletions rplugin/node/magenta/src/anthropic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Anthropic from "@anthropic-ai/sdk";
import { Logger } from "./logger.ts";
import { TOOLS, ToolRequest } from "./tools/index.ts";
import { TOOL_SPECS, ToolRequest } from "./tools/toolManager.ts";

export class AnthropicClient {
private client: Anthropic;
Expand Down Expand Up @@ -53,7 +53,7 @@ export class AnthropicClient {
type: "auto",
disable_parallel_tool_use: false,
},
tools: Object.values(TOOLS).map((t) => t.spec()),
tools: TOOL_SPECS,
})
.on("text", (text: string) => {
buf.push(text);
Expand Down
6 changes: 2 additions & 4 deletions rplugin/node/magenta/src/chat/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import {
update as updateMessage,
view as messageView,
} from "./message.ts";
import { ToolRequest } from "../tools/index.ts";
import { ToolRequest } from "../tools/toolManager.ts";
import { Dispatch, Update } from "../tea/tea.ts";
import { ToolProcess } from "../tools/types.ts";
import { d, View } from "../tea/view.ts";

export type Role = "user" | "assistant";
Expand Down Expand Up @@ -47,7 +46,6 @@ export type Msg =
| {
type: "add-tool-use";
request: ToolRequest;
process: ToolProcess;
}
| {
type: "add-tool-response";
Expand Down Expand Up @@ -108,7 +106,7 @@ export const update: Update<Msg, Model> = (msg, model) => {
});
}
const [nextMessage] = updateMessage(
{ type: "add-tool-use", request: msg.request, process: msg.process },
{ type: "add-tool-use", request: msg.request },
model.messages[model.messages.length - 1],
);
model.messages[model.messages.length - 1] = nextMessage;
Expand Down
5 changes: 1 addition & 4 deletions rplugin/node/magenta/src/chat/message.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ToolResultBlockParam } from "@anthropic-ai/sdk/resources/index.mjs";
import { Model as Part, view as partView } from "./part.ts";
import { ToolProcess } from "../tools/types.ts";
import { ToolRequest } from "../tools/index.ts";
import { ToolRequest } from "../tools/toolManager.ts";
import { Role } from "./chat.ts";
import { Dispatch, Update } from "../tea/tea.ts";
import { assertUnreachable } from "../utils/assertUnreachable.ts";
Expand All @@ -20,7 +19,6 @@ export type Msg =
| {
type: "add-tool-use";
request: ToolRequest;
process: ToolProcess;
}
| {
type: "add-tool-response";
Expand All @@ -47,7 +45,6 @@ export const update: Update<Msg, Model> = (msg, model) => {
model.parts.push({
type: "tool-use",
request: msg.request,
process: msg.process,
});
break;

Expand Down
16 changes: 1 addition & 15 deletions rplugin/node/magenta/src/chat/part.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
import Anthropic from "@anthropic-ai/sdk";
import { ToolRequest } from "../tools/index.ts";
import { ToolRequest } from "../tools/toolManager.ts";
import { assertUnreachable } from "../utils/assertUnreachable.ts";
import { ToolProcess } from "../tools/types.ts";
import { d, View } from "../tea/view.ts";

/** A line that's meant to be sent to neovim. Should not contain newlines
*/
export type Line = string & { __line: true };

export type ToolUseState =
| {
state: "processing";
}
| {
state: "pending-approval";
}
| {
state: "done";
response: Anthropic.ToolResultBlockParam;
};

export type Model =
| {
type: "text";
Expand All @@ -28,7 +15,6 @@ export type Model =
| {
type: "tool-use";
request: ToolRequest;
process: ToolProcess;
}
| {
type: "tool-response";
Expand Down
95 changes: 48 additions & 47 deletions rplugin/node/magenta/src/magenta.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
import { AnthropicClient } from "./anthropic.ts";
import { NvimPlugin } from "neovim";
import { Sidebar } from "./sidebar.ts";
import {
Model as ChatModel,
Msg as ChatMsg,
getMessages,
view as chatView,
update as chatUpdate,
} from "./chat/chat.ts";
import * as Chat from "./chat/chat.ts";
import { Logger } from "./logger.ts";
import { Context } from "./types.ts";
import { TOOLS } from "./tools/index.ts";
import { assertUnreachable } from "./utils/assertUnreachable.ts";
import { ToolProcess } from "./tools/types.ts";
import { Moderator } from "./moderator.ts";
import { App, createApp } from "./tea/tea.ts";
import * as ToolManager from "./tools/toolManager.ts";
import { d } from "./tea/view.ts";

class Magenta {
private anthropicClient: AnthropicClient;
private sidebar: Sidebar;
private moderator: Moderator;
private chat: App<ChatMsg, ChatModel>;
private chat: App<Chat.Msg, Chat.Model>;
private toolManager: App<ToolManager.Msg, ToolManager.Model>;

constructor(private context: Context) {
this.context.logger.debug(`Initializing plugin`);
this.anthropicClient = new AnthropicClient(this.context.logger);
this.sidebar = new Sidebar(this.context.nvim, this.context.logger);

this.chat = createApp({
initialModel: { messages: [] },
update: chatUpdate,
View: chatView,
update: Chat.update,
View: Chat.view,
context,
});
this.moderator = new Moderator(
this.context,
// on tool result
(request, response) => {
this.chat.dispatch({
type: "add-tool-response",
request,
response,
});
},
// autorespond
() => {
this.sendMessage().catch((err) =>
this.context.logger.error(err as Error),
);

this.toolManager = createApp({
initialModel: ToolManager.initModel(),
update: ToolManager.update,
View: () => d``,
context,
onUpdate: (msg, model) => {
if (msg.type == "tool-msg") {
if (msg.msg.msg.type == "finish") {
const toolModel = model.toolModels[msg.id];
const response = msg.msg.msg.result;
this.chat.dispatch({
type: "add-tool-response",
request: toolModel.request,
response,
});

if (toolModel.autoRespond) {
let shouldRespond = true;
for (const tool of Object.values(model.toolModels)) {
if (tool.state.state != "done") {
shouldRespond = false;
break;
}
}

if (shouldRespond) {
this.sendMessage().catch((err) =>
this.context.logger.error(err as Error),
);
}
}
}
}
},
);
});
}

async command(args: string[]): Promise<void> {
Expand Down Expand Up @@ -100,7 +112,7 @@ class Magenta {
return;
}

const messages = getMessages(state.model);
const messages = Chat.getMessages(state.model);

const toolRequests = await this.anthropicClient.sendMessage(
messages,
Expand All @@ -115,25 +127,14 @@ class Magenta {

if (toolRequests.length) {
for (const request of toolRequests) {
let process: ToolProcess;
switch (request.name) {
case "get_file": {
process = TOOLS[request.name].execRequest(request, this.context);
break;
}
case "insert": {
process = TOOLS[request.name].execRequest(request, this.context);
break;
}
default:
assertUnreachable(request);
}
this.toolManager.dispatch({
type: "init-tool-use",
request,
});

this.moderator.registerProcess(process);
this.chat.dispatch({
type: "add-tool-use",
request,
process,
});
}
}
Expand Down
70 changes: 0 additions & 70 deletions rplugin/node/magenta/src/moderator.ts

This file was deleted.

Loading

0 comments on commit 5100c98

Please sign in to comment.