feat: implement tool registry and add various tools for HTTP, file operations, SSH, and Matrix messaging

This commit is contained in:
2026-03-04 21:10:29 +00:00
parent ddec55871b
commit 0f8d2f9ca0
11 changed files with 828 additions and 45 deletions
+76 -13
View File
@@ -71,8 +71,8 @@ type anthropicRequest struct {
}
type anthropicMessage struct {
Role string `json:"role"`
Content string `json:"content"`
Role string `json:"role"`
Content json.RawMessage `json:"content"`
}
type anthropicTool struct {
@@ -81,12 +81,26 @@ type anthropicTool struct {
InputSchema map[string]any `json:"input_schema"`
}
// anthropicContentBlock represents a block in a content array.
type anthropicContentBlock struct {
Type string `json:"type"`
// text block
Text string `json:"text,omitempty"`
// tool_use block (in assistant responses)
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Input map[string]any `json:"input,omitempty"`
// tool_result block (in user messages)
ToolUseID string `json:"tool_use_id,omitempty"`
Content string `json:"content,omitempty"`
}
type anthropicResponse struct {
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"content"`
Usage struct {
Content []anthropicContentBlock `json:"content"`
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
@@ -97,12 +111,9 @@ func toAnthropicRequest(req coretypes.CompletionRequest) anthropicRequest {
msgs := make([]anthropicMessage, 0, len(req.Messages))
for _, m := range req.Messages {
if m.Role == coretypes.RoleSystem {
continue // handled as top-level system param
continue
}
msgs = append(msgs, anthropicMessage{
Role: string(m.Role),
Content: m.Content,
})
msgs = append(msgs, toAnthropicMessage(m))
}
tools := make([]anthropicTool, len(req.Tools))
@@ -123,19 +134,71 @@ func toAnthropicRequest(req coretypes.CompletionRequest) anthropicRequest {
}
}
// toAnthropicMessage converts a core Message to the Anthropic format.
// Handles plain text, assistant messages with tool calls, and tool result messages.
func toAnthropicMessage(m coretypes.Message) anthropicMessage {
// Assistant message with tool calls → content array with text + tool_use blocks
if m.Role == coretypes.RoleAssistant && len(m.ToolCalls) > 0 {
blocks := make([]anthropicContentBlock, 0, len(m.ToolCalls)+1)
if m.Content != "" {
blocks = append(blocks, anthropicContentBlock{Type: "text", Text: m.Content})
}
for _, tc := range m.ToolCalls {
var input map[string]any
_ = json.Unmarshal([]byte(tc.Arguments), &input)
blocks = append(blocks, anthropicContentBlock{
Type: "tool_use",
ID: tc.ID,
Name: tc.Name,
Input: input,
})
}
raw, _ := json.Marshal(blocks)
return anthropicMessage{Role: "assistant", Content: raw}
}
// Tool result message → user message with tool_result content array
if m.Role == coretypes.RoleTool {
blocks := []anthropicContentBlock{{
Type: "tool_result",
ToolUseID: m.ToolCallID,
Content: m.Content,
}}
raw, _ := json.Marshal(blocks)
return anthropicMessage{Role: "user", Content: raw}
}
// Plain text message
raw, _ := json.Marshal(m.Content)
return anthropicMessage{Role: string(m.Role), Content: raw}
}
func fromAnthropicResponse(raw []byte) (coretypes.CompletionResponse, error) {
var ar anthropicResponse
if err := json.Unmarshal(raw, &ar); err != nil {
return coretypes.CompletionResponse{}, fmt.Errorf("unmarshal response: %w", err)
}
var content string
var toolCalls []coretypes.ToolCall
for _, c := range ar.Content {
if c.Type == "text" {
switch c.Type {
case "text":
content += c.Text
case "tool_use":
argsJSON, _ := json.Marshal(c.Input)
toolCalls = append(toolCalls, coretypes.ToolCall{
ID: c.ID,
Name: c.Name,
Arguments: string(argsJSON),
})
}
}
return coretypes.CompletionResponse{
Content: content,
ToolCalls: toolCalls,
FinishReason: ar.StopReason,
Usage: coretypes.TokenUsage{
InputTokens: ar.Usage.InputTokens,