generated from ellisonleao/nvim-plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
365 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
import * as Anthropic from "@anthropic-ai/sdk"; | ||
import { assertUnreachable } from "../utils/assertUnreachable.ts"; | ||
import { ToolResultBlockParam } from "@anthropic-ai/sdk/resources/index.mjs"; | ||
import { Dispatch, Update } from "../tea/tea.ts"; | ||
import { d, VDOMNode, withBindings } from "../tea/view.ts"; | ||
import { ToolRequestId } from "./toolManager.ts"; | ||
import { displayDiffs } from "./diff.ts"; | ||
|
||
export type Model = { | ||
type: "replace"; | ||
autoRespond: boolean; | ||
request: ReplaceToolRequest; | ||
state: | ||
| { | ||
state: "pending-user-action"; | ||
} | ||
| { | ||
state: "editing-diff"; | ||
} | ||
| { | ||
state: "done"; | ||
result: ToolResultBlockParam; | ||
}; | ||
}; | ||
|
||
export type Msg = | ||
| { | ||
type: "finish"; | ||
result: ToolResultBlockParam; | ||
} | ||
| { | ||
type: "display-diff"; | ||
}; | ||
|
||
export const update: Update<Msg, Model> = (msg, model) => { | ||
switch (msg.type) { | ||
case "finish": | ||
return [ | ||
{ | ||
...model, | ||
state: { | ||
state: "done", | ||
result: msg.result, | ||
}, | ||
}, | ||
]; | ||
case "display-diff": | ||
return [ | ||
{ | ||
...model, | ||
state: { | ||
state: "pending-user-action", | ||
}, | ||
}, | ||
insertThunk(model), | ||
]; | ||
default: | ||
assertUnreachable(msg); | ||
} | ||
}; | ||
|
||
export function initModel(request: ReplaceToolRequest): [Model] { | ||
const model: Model = { | ||
type: "replace", | ||
autoRespond: false, | ||
request, | ||
state: { | ||
state: "pending-user-action", | ||
}, | ||
}; | ||
|
||
return [model]; | ||
} | ||
|
||
export function insertThunk(model: Model) { | ||
const request = model.request; | ||
return async (dispatch: Dispatch<Msg>) => { | ||
try { | ||
await displayDiffs( | ||
request.input.filePath, | ||
[ | ||
{ | ||
type: "replace", | ||
start: request.input.start, | ||
end: request.input.end, | ||
content: request.input.content, | ||
}, | ||
], | ||
(msg) => | ||
dispatch({ | ||
type: "finish", | ||
result: { | ||
type: "tool_result", | ||
tool_use_id: model.request.id, | ||
content: msg.error, | ||
is_error: true, | ||
}, | ||
}), | ||
); | ||
} catch (error) { | ||
dispatch({ | ||
type: "finish", | ||
result: { | ||
type: "tool_result", | ||
tool_use_id: request.id, | ||
content: `Error: ${(error as Error).message}`, | ||
is_error: true, | ||
}, | ||
}); | ||
} | ||
}; | ||
} | ||
|
||
export function view({ | ||
model, | ||
dispatch, | ||
}: { | ||
model: Model; | ||
dispatch: Dispatch<Msg>; | ||
}): VDOMNode { | ||
return d`Insert ${( | ||
model.request.input.content.match(/\n/g) || [] | ||
).length.toString()} into file ${model.request.input.filePath} | ||
${toolStatusView({ model, dispatch })}`; | ||
} | ||
|
||
function toolStatusView({ | ||
model, | ||
dispatch, | ||
}: { | ||
model: Model; | ||
dispatch: Dispatch<Msg>; | ||
}): VDOMNode { | ||
switch (model.state.state) { | ||
case "pending-user-action": | ||
return withBindings(d`[review diff]`, { | ||
Enter: () => | ||
dispatch({ | ||
type: "display-diff", | ||
}), | ||
}); | ||
case "editing-diff": | ||
return d`Editing diff`; | ||
case "done": | ||
return d`Done`; | ||
} | ||
} | ||
|
||
export function getToolResult(model: Model): ToolResultBlockParam { | ||
switch (model.state.state) { | ||
case "editing-diff": | ||
return { | ||
type: "tool_result", | ||
tool_use_id: model.request.id, | ||
content: `The user is reviewing the change. Please proceed with your answer or address other parts of the question.`, | ||
}; | ||
case "pending-user-action": | ||
return { | ||
type: "tool_result", | ||
tool_use_id: model.request.id, | ||
content: `Waiting for a user action to finish processing this tool use. Please proceed with your answer or address other parts of the question.`, | ||
}; | ||
case "done": | ||
return model.state.result; | ||
default: | ||
assertUnreachable(model.state); | ||
} | ||
} | ||
|
||
export const spec: Anthropic.Anthropic.Tool = { | ||
name: "insert", | ||
description: "Replace text between two strings in a file.", | ||
input_schema: { | ||
type: "object", | ||
properties: { | ||
filePath: { | ||
type: "string", | ||
description: "Path of the file to modify.", | ||
}, | ||
start: { | ||
type: "string", | ||
description: | ||
"We will replace text starting with this string. This string is included in the text that is replaced. Please provide a minimal string that uniquely identifies a location in the file.", | ||
}, | ||
end: { | ||
type: "string", | ||
description: | ||
"We will replace text until we encounter this string. This string is included in the text that is replaced. Please provide a minimal string that uniquely identifies a location in the file.", | ||
}, | ||
content: { | ||
type: "string", | ||
description: "Content to insert", | ||
}, | ||
}, | ||
required: ["filePath", "start", "end", "content"], | ||
}, | ||
}; | ||
|
||
export type ReplaceToolRequest = { | ||
type: "tool_use"; | ||
id: ToolRequestId; | ||
name: "replace"; | ||
input: { | ||
filePath: string; | ||
start: string; | ||
end: string; | ||
content: string; | ||
}; | ||
}; |
Oops, something went wrong.