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
+81 -15
View File
@@ -2,6 +2,7 @@ package llm
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -33,19 +34,7 @@ func NewOpenAIComplete(apiKeyEnv, baseURL string) coretypes.CompleteFunc {
})
}
for _, m := range req.Messages {
role := openai.ChatMessageRoleUser
switch m.Role {
case coretypes.RoleAssistant:
role = openai.ChatMessageRoleAssistant
case coretypes.RoleSystem:
role = openai.ChatMessageRoleSystem
case coretypes.RoleTool:
role = openai.ChatMessageRoleTool
}
msgs = append(msgs, openai.ChatCompletionMessage{
Role: role,
Content: m.Content,
})
msgs = append(msgs, toOpenAIMessage(m))
}
openReq := openai.ChatCompletionRequest{
@@ -55,6 +44,11 @@ func NewOpenAIComplete(apiKeyEnv, baseURL string) coretypes.CompleteFunc {
Temperature: float32(req.Temperature),
}
// Add tools if present
if len(req.Tools) > 0 {
openReq.Tools = toOpenAITools(req.Tools)
}
resp, err := client.CreateChatCompletion(ctx, openReq)
if err != nil {
return coretypes.CompletionResponse{}, fmt.Errorf("openai completion: %w", err)
@@ -63,9 +57,20 @@ func NewOpenAIComplete(apiKeyEnv, baseURL string) coretypes.CompleteFunc {
return coretypes.CompletionResponse{}, fmt.Errorf("openai: empty choices")
}
choice := resp.Choices[0]
var toolCalls []coretypes.ToolCall
for _, tc := range choice.Message.ToolCalls {
toolCalls = append(toolCalls, coretypes.ToolCall{
ID: tc.ID,
Name: tc.Function.Name,
Arguments: tc.Function.Arguments,
})
}
return coretypes.CompletionResponse{
Content: resp.Choices[0].Message.Content,
FinishReason: string(resp.Choices[0].FinishReason),
Content: choice.Message.Content,
ToolCalls: toolCalls,
FinishReason: string(choice.FinishReason),
Usage: coretypes.TokenUsage{
InputTokens: resp.Usage.PromptTokens,
OutputTokens: resp.Usage.CompletionTokens,
@@ -74,3 +79,64 @@ func NewOpenAIComplete(apiKeyEnv, baseURL string) coretypes.CompleteFunc {
}, nil
}
}
// toOpenAIMessage converts a core Message to an OpenAI ChatCompletionMessage.
func toOpenAIMessage(m coretypes.Message) openai.ChatCompletionMessage {
role := openai.ChatMessageRoleUser
switch m.Role {
case coretypes.RoleAssistant:
role = openai.ChatMessageRoleAssistant
case coretypes.RoleSystem:
role = openai.ChatMessageRoleSystem
case coretypes.RoleTool:
role = openai.ChatMessageRoleTool
}
msg := openai.ChatCompletionMessage{
Role: role,
Content: m.Content,
ToolCallID: m.ToolCallID,
}
// Assistant messages with tool calls
if m.Role == coretypes.RoleAssistant && len(m.ToolCalls) > 0 {
msg.ToolCalls = make([]openai.ToolCall, len(m.ToolCalls))
for i, tc := range m.ToolCalls {
msg.ToolCalls[i] = openai.ToolCall{
ID: tc.ID,
Type: openai.ToolTypeFunction,
Function: openai.FunctionCall{
Name: tc.Name,
Arguments: tc.Arguments,
},
}
}
}
return msg
}
// toOpenAITools converts core ToolSpecs to OpenAI Tool format.
func toOpenAITools(specs []coretypes.ToolSpec) []openai.Tool {
tools := make([]openai.Tool, len(specs))
for i, s := range specs {
tools[i] = openai.Tool{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: s.Name,
Description: s.Description,
Parameters: json.RawMessage(marshalSchema(s.InputSchema)),
},
}
}
return tools
}
// marshalSchema marshals a JSON schema map to bytes. Falls back to empty object.
func marshalSchema(schema map[string]any) []byte {
b, err := json.Marshal(schema)
if err != nil {
return []byte("{}")
}
return b
}