summaryrefslogtreecommitdiffhomepage
path: root/internal/llm
diff options
context:
space:
mode:
Diffstat (limited to 'internal/llm')
-rw-r--r--internal/llm/agent/agent.go22
-rw-r--r--internal/llm/provider/anthropic.go60
-rw-r--r--internal/llm/provider/openai.go9
-rw-r--r--internal/llm/provider/provider.go7
4 files changed, 78 insertions, 20 deletions
diff --git a/internal/llm/agent/agent.go b/internal/llm/agent/agent.go
index 7542d9adf..ae5bcb231 100644
--- a/internal/llm/agent/agent.go
+++ b/internal/llm/agent/agent.go
@@ -380,6 +380,21 @@ func (a *agent) processEvent(ctx context.Context, sessionID string, assistantMsg
case provider.EventContentDelta:
assistantMsg.AppendContent(event.Content)
return a.messages.Update(ctx, *assistantMsg)
+ case provider.EventToolUseStart:
+ assistantMsg.AddToolCall(*event.ToolCall)
+ return a.messages.Update(ctx, *assistantMsg)
+ // TODO: see how to handle this
+ // case provider.EventToolUseDelta:
+ // tm := time.Unix(assistantMsg.UpdatedAt, 0)
+ // assistantMsg.AppendToolCallInput(event.ToolCall.ID, event.ToolCall.Input)
+ // if time.Since(tm) > 1000*time.Millisecond {
+ // err := a.messages.Update(ctx, *assistantMsg)
+ // assistantMsg.UpdatedAt = time.Now().Unix()
+ // return err
+ // }
+ case provider.EventToolUseStop:
+ assistantMsg.FinishToolCall(event.ToolCall.ID)
+ return a.messages.Update(ctx, *assistantMsg)
case provider.EventError:
if errors.Is(event.Error, context.Canceled) {
logging.InfoPersist(fmt.Sprintf("Event processing canceled for session: %s", sessionID))
@@ -456,6 +471,13 @@ func createAgentProvider(agentName config.AgentName) (provider.Provider, error)
provider.WithReasoningEffort(agentConfig.ReasoningEffort),
),
)
+ } else if model.Provider == models.ProviderAnthropic && model.CanReason {
+ opts = append(
+ opts,
+ provider.WithAnthropicOptions(
+ provider.WithAnthropicShouldThinkFn(provider.DefaultShouldThinkFn),
+ ),
+ )
}
agentProvider, err := provider.NewProvider(
model.Provider,
diff --git a/internal/llm/provider/anthropic.go b/internal/llm/provider/anthropic.go
index 7bbc02103..2c16a0593 100644
--- a/internal/llm/provider/anthropic.go
+++ b/internal/llm/provider/anthropic.go
@@ -93,8 +93,7 @@ func (a *anthropicClient) convertMessages(messages []message.Message) (anthropic
}
if len(blocks) == 0 {
- logging.Warn("There is a message without content, investigate")
- // This should never happend but we log this because we might have a bug in our cleanup method
+ logging.Warn("There is a message without content, investigate, this should not happen")
continue
}
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(blocks...))
@@ -196,8 +195,8 @@ func (a *anthropicClient) send(ctx context.Context, messages []message.Message,
preparedMessages := a.preparedMessages(a.convertMessages(messages), a.convertTools(tools))
cfg := config.Get()
if cfg.Debug {
- jsonData, _ := json.Marshal(preparedMessages)
- logging.Debug("Prepared messages", "messages", string(jsonData))
+ // jsonData, _ := json.Marshal(preparedMessages)
+ // logging.Debug("Prepared messages", "messages", string(jsonData))
}
attempts := 0
for {
@@ -243,8 +242,8 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message
preparedMessages := a.preparedMessages(a.convertMessages(messages), a.convertTools(tools))
cfg := config.Get()
if cfg.Debug {
- jsonData, _ := json.Marshal(preparedMessages)
- logging.Debug("Prepared messages", "messages", string(jsonData))
+ // jsonData, _ := json.Marshal(preparedMessages)
+ // logging.Debug("Prepared messages", "messages", string(jsonData))
}
attempts := 0
eventChan := make(chan ProviderEvent)
@@ -257,6 +256,7 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message
)
accumulatedMessage := anthropic.Message{}
+ currentToolCallID := ""
for anthropicStream.Next() {
event := anthropicStream.Current()
err := accumulatedMessage.Accumulate(event)
@@ -267,7 +267,19 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message
switch event := event.AsAny().(type) {
case anthropic.ContentBlockStartEvent:
- eventChan <- ProviderEvent{Type: EventContentStart}
+ if event.ContentBlock.Type == "text" {
+ eventChan <- ProviderEvent{Type: EventContentStart}
+ } else if event.ContentBlock.Type == "tool_use" {
+ currentToolCallID = event.ContentBlock.ID
+ eventChan <- ProviderEvent{
+ Type: EventToolUseStart,
+ ToolCall: &message.ToolCall{
+ ID: event.ContentBlock.ID,
+ Name: event.ContentBlock.Name,
+ Finished: false,
+ },
+ }
+ }
case anthropic.ContentBlockDeltaEvent:
if event.Delta.Type == "thinking_delta" && event.Delta.Thinking != "" {
@@ -280,11 +292,30 @@ func (a *anthropicClient) stream(ctx context.Context, messages []message.Message
Type: EventContentDelta,
Content: event.Delta.Text,
}
+ } else if event.Delta.Type == "input_json_delta" {
+ if currentToolCallID != "" {
+ eventChan <- ProviderEvent{
+ Type: EventToolUseDelta,
+ ToolCall: &message.ToolCall{
+ ID: currentToolCallID,
+ Finished: false,
+ Input: event.Delta.JSON.PartialJSON.Raw(),
+ },
+ }
+ }
}
- // TODO: check if we can somehow stream tool calls
-
case anthropic.ContentBlockStopEvent:
- eventChan <- ProviderEvent{Type: EventContentStop}
+ if currentToolCallID != "" {
+ eventChan <- ProviderEvent{
+ Type: EventToolUseStop,
+ ToolCall: &message.ToolCall{
+ ID: currentToolCallID,
+ },
+ }
+ currentToolCallID = ""
+ } else {
+ eventChan <- ProviderEvent{Type: EventContentStop}
+ }
case anthropic.MessageStopEvent:
content := ""
@@ -378,10 +409,11 @@ func (a *anthropicClient) toolCalls(msg anthropic.Message) []message.ToolCall {
switch variant := block.AsAny().(type) {
case anthropic.ToolUseBlock:
toolCall := message.ToolCall{
- ID: variant.ID,
- Name: variant.Name,
- Input: string(variant.Input),
- Type: string(variant.Type),
+ ID: variant.ID,
+ Name: variant.Name,
+ Input: string(variant.Input),
+ Type: string(variant.Type),
+ Finished: true,
}
toolCalls = append(toolCalls, toolCall)
}
diff --git a/internal/llm/provider/openai.go b/internal/llm/provider/openai.go
index 6c6f74988..40d263242 100644
--- a/internal/llm/provider/openai.go
+++ b/internal/llm/provider/openai.go
@@ -344,10 +344,11 @@ func (o *openaiClient) toolCalls(completion openai.ChatCompletion) []message.Too
if len(completion.Choices) > 0 && len(completion.Choices[0].Message.ToolCalls) > 0 {
for _, call := range completion.Choices[0].Message.ToolCalls {
toolCall := message.ToolCall{
- ID: call.ID,
- Name: call.Function.Name,
- Input: call.Function.Arguments,
- Type: "function",
+ ID: call.ID,
+ Name: call.Function.Name,
+ Input: call.Function.Arguments,
+ Type: "function",
+ Finished: true,
}
toolCalls = append(toolCalls, toolCall)
}
diff --git a/internal/llm/provider/provider.go b/internal/llm/provider/provider.go
index e04bee71b..283a0d983 100644
--- a/internal/llm/provider/provider.go
+++ b/internal/llm/provider/provider.go
@@ -15,6 +15,9 @@ const maxRetries = 8
const (
EventContentStart EventType = "content_start"
+ EventToolUseStart EventType = "tool_use_start"
+ EventToolUseDelta EventType = "tool_use_delta"
+ EventToolUseStop EventType = "tool_use_stop"
EventContentDelta EventType = "content_delta"
EventThinkingDelta EventType = "thinking_delta"
EventContentStop EventType = "content_stop"
@@ -43,8 +46,8 @@ type ProviderEvent struct {
Content string
Thinking string
Response *ProviderResponse
-
- Error error
+ ToolCall *message.ToolCall
+ Error error
}
type Provider interface {
SendMessages(ctx context.Context, messages []message.Message, tools []tools.BaseTool) (*ProviderResponse, error)