Files
odidere/internal/llm/client_tools.go
dwrz f6fb21d40c Replace go-openai with native client
Drop the github.com/sashabaranov/go-openai dependency in favor of an
internal HTTP client.

The LLM package is now split into:
- llm.go: type definitions (Message, ChatRequest, ChatResponse, etc.)
- client.go: client construction and configuration
- client_completions.go: Completions and CompletionsStream methods
- client_models.go: ListModels
- client_tools.go: tool call execution

CompletionsStream uses typed SSE events (delta, done, message) so the
frontend can render progressive streaming output.

Move tool.go and tool_test.go from internal/tool/ into internal/llm/
to co-locate the registry with the client that consumes it.

Update job.go and service.go to use the new llm.Message types and
Completions/CompletionsStream methods. Remove the Voice field from
chat request/response (voice is now tracked via message metadata).

Frontend: handle delta/done/message SSE events, render a streaming
placeholder that receives progressive text deltas, and finalize with
full message binding (reasoning, tool calls, actions).
2026-06-26 14:22:05 +00:00

62 lines
1.2 KiB
Go

package llm
import (
"context"
"fmt"
"log/slog"
"strings"
)
// callTools executes each tool call in the message and returns the
// resulting tool result messages.
func (c *Client) callTools(ctx context.Context, msg Message) (
results []Message, err error,
) {
for _, tc := range msg.ToolCalls {
c.log.InfoContext(
ctx,
"calling tool",
slog.String("name", tc.Function.Name),
slog.String("args", tc.Function.Arguments),
)
result, err := c.registry.Execute(
ctx, tc.Function.Name, tc.Function.Arguments,
)
if err != nil {
c.log.ErrorContext(
ctx,
"failed to call tool",
slog.Any("error", err),
slog.String("name", tc.Function.Name),
)
result = fmt.Sprintf(
`{"ok": false, "error": %q}`, err,
)
} else {
c.log.InfoContext(
ctx,
"called tool",
slog.String("name", tc.Function.Name),
)
}
// Content cannot be empty.
if strings.TrimSpace(result) == "" {
result = `{"ok": true, "result": null}`
}
toolResult := Message{
Role: RoleTool,
ContentParts: []ContentPart{{
Type: ContentTypeText, Text: result,
}},
Name: tc.Function.Name,
ToolCallID: tc.ID,
}
results = append(results, toolResult)
}
return results, nil
}