Files
odidere/internal/llm/client_tools.go

78 lines
1.6 KiB
Go
Raw Normal View History

package llm
import (
"context"
2026-07-08 01:59:11 +00:00
"runtime"
"sync"
2026-07-08 11:52:45 +00:00
"log/slog"
)
2026-07-08 01:59:11 +00:00
// callTools executes each tool call in the message concurrently and returns
// the resulting tool result messages in the same order as the input calls.
func (c *Client) callTools(ctx context.Context, msg Message) []Message {
var (
results = make([]Message, len(msg.ToolCalls))
sem = make(chan struct{}, runtime.NumCPU()*2)
wg sync.WaitGroup
)
for i, tc := range msg.ToolCalls {
wg.Add(1)
go func(idx int, tc ToolCall) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
c.log.InfoContext(
ctx,
2026-07-08 01:59:11 +00:00
"calling tool",
slog.String("name", tc.Function.Name),
2026-07-08 01:59:11 +00:00
slog.String("args", tc.Function.Arguments),
)
2026-07-08 01:59:11 +00:00
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),
)
2026-07-08 11:52:45 +00:00
results[idx] = Message{
Role: RoleTool,
ContentParts: []ContentPart{{
Type: ContentTypeText,
Text: err.Error(),
}},
Name: tc.Function.Name,
ToolCallID: tc.ID,
}
return
2026-07-08 01:59:11 +00:00
}
2026-07-08 11:52:45 +00:00
c.log.InfoContext(
ctx,
"called tool",
slog.String("name", tc.Function.Name),
)
formatted := result.FormatResult()
2026-07-08 01:59:11 +00:00
results[idx] = Message{
Role: RoleTool,
ContentParts: []ContentPart{{
2026-07-08 11:52:45 +00:00
Type: ContentTypeText,
Text: formatted,
2026-07-08 01:59:11 +00:00
}},
Name: tc.Function.Name,
ToolCallID: tc.ID,
}
}(i, tc)
}
2026-07-08 01:59:11 +00:00
wg.Wait()
return results
}