SuperCoder/internal/llm/agent/agent.go

671 lines
19 KiB
Go
Raw Normal View History

2025-03-23 22:25:31 +01:00
package agent
import (
"context"
2025-03-27 22:35:48 +01:00
"errors"
2025-03-23 22:25:31 +01:00
"fmt"
2025-04-03 15:20:15 +02:00
"strings"
2025-03-27 22:35:48 +01:00
"sync"
2025-03-23 22:25:31 +01:00
2025-03-27 22:35:48 +01:00
"github.com/kujtimiihoxha/termai/internal/config"
"github.com/kujtimiihoxha/termai/internal/llm/models"
"github.com/kujtimiihoxha/termai/internal/llm/prompt"
"github.com/kujtimiihoxha/termai/internal/llm/provider"
"github.com/kujtimiihoxha/termai/internal/llm/tools"
2025-04-10 13:29:44 +02:00
"github.com/kujtimiihoxha/termai/internal/logging"
2025-03-27 22:35:48 +01:00
"github.com/kujtimiihoxha/termai/internal/message"
2025-04-13 14:37:05 +02:00
"github.com/kujtimiihoxha/termai/internal/session"
2025-03-23 22:25:31 +01:00
)
2025-04-13 14:37:05 +02:00
// Common errors
var (
ErrProviderNotEnabled = errors.New("provider is not enabled")
ErrRequestCancelled = errors.New("request cancelled by user")
ErrSessionBusy = errors.New("session is currently processing another request")
)
// Service defines the interface for generating responses
type Service interface {
2025-04-10 13:29:44 +02:00
Generate(ctx context.Context, sessionID string, content string) error
2025-04-13 14:37:05 +02:00
Cancel(sessionID string) error
2025-03-27 22:35:48 +01:00
}
type agent struct {
2025-04-13 14:37:05 +02:00
sessions session.Service
messages message.Service
2025-03-27 22:35:48 +01:00
model models.Model
tools []tools.BaseTool
agent provider.Provider
titleGenerator provider.Provider
2025-04-13 14:37:05 +02:00
activeRequests sync.Map // map[sessionID]context.CancelFunc
}
// NewAgent creates a new agent instance with the given model and tools
func NewAgent(ctx context.Context, sessions session.Service, messages message.Service, model models.Model, tools []tools.BaseTool) (Service, error) {
agentProvider, titleGenerator, err := getAgentProviders(ctx, model)
if err != nil {
return nil, fmt.Errorf("failed to initialize providers: %w", err)
}
return &agent{
model: model,
tools: tools,
sessions: sessions,
messages: messages,
agent: agentProvider,
titleGenerator: titleGenerator,
activeRequests: sync.Map{},
}, nil
}
// Cancel cancels an active request by session ID
func (a *agent) Cancel(sessionID string) error {
if cancelFunc, exists := a.activeRequests.LoadAndDelete(sessionID); exists {
if cancel, ok := cancelFunc.(context.CancelFunc); ok {
logging.InfoPersist(fmt.Sprintf("Request cancellation initiated for session: %s", sessionID))
cancel()
return nil
}
}
return errors.New("no active request found for this session")
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
// Generate starts the generation process
func (a *agent) Generate(ctx context.Context, sessionID string, content string) error {
// Check if this session already has an active request
if _, busy := a.activeRequests.Load(sessionID); busy {
return ErrSessionBusy
}
// Create a cancellable context
genCtx, cancel := context.WithCancel(ctx)
// Store cancel function to allow user cancellation
a.activeRequests.Store(sessionID, cancel)
// Launch the generation in a goroutine
go func() {
defer func() {
if r := recover(); r != nil {
logging.ErrorPersist(fmt.Sprintf("Panic in Generate: %v", r))
}
}()
defer a.activeRequests.Delete(sessionID)
defer cancel()
if err := a.generate(genCtx, sessionID, content); err != nil {
if !errors.Is(err, ErrRequestCancelled) && !errors.Is(err, context.Canceled) {
// Log the error (avoid logging cancellations as they're expected)
logging.ErrorPersist(fmt.Sprintf("Generation error for session %s: %v", sessionID, err))
// You may want to create an error message in the chat
bgCtx := context.Background()
errorMsg := fmt.Sprintf("Sorry, an error occurred: %v", err)
_, createErr := a.messages.Create(bgCtx, sessionID, message.CreateMessageParams{
Role: message.System,
Parts: []message.ContentPart{
message.TextContent{
Text: errorMsg,
},
},
})
if createErr != nil {
logging.ErrorPersist(fmt.Sprintf("Failed to create error message: %v", createErr))
}
}
}
}()
return nil
}
// IsSessionBusy checks if a session currently has an active request
func (a *agent) IsSessionBusy(sessionID string) bool {
_, busy := a.activeRequests.Load(sessionID)
return busy
} // handleTitleGeneration asynchronously generates a title for new sessions
func (a *agent) handleTitleGeneration(ctx context.Context, sessionID, content string) {
response, err := a.titleGenerator.SendMessages(
2025-04-10 13:29:44 +02:00
ctx,
2025-03-27 22:35:48 +01:00
[]message.Message{
{
2025-04-03 15:20:15 +02:00
Role: message.User,
Parts: []message.ContentPart{
message.TextContent{
Text: content,
},
},
2025-03-27 22:35:48 +01:00
},
},
nil,
)
if err != nil {
2025-04-13 14:37:05 +02:00
logging.ErrorPersist(fmt.Sprintf("Failed to generate title: %v", err))
2025-03-27 22:35:48 +01:00
return
}
2025-04-13 14:37:05 +02:00
session, err := a.sessions.Get(ctx, sessionID)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-13 14:37:05 +02:00
logging.ErrorPersist(fmt.Sprintf("Failed to get session: %v", err))
2025-03-27 22:35:48 +01:00
return
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
if response.Content != "" {
2025-04-13 14:37:05 +02:00
session.Title = strings.TrimSpace(response.Content)
2025-04-03 15:20:15 +02:00
session.Title = strings.ReplaceAll(session.Title, "\n", " ")
2025-04-13 14:37:05 +02:00
if _, err := a.sessions.Save(ctx, session); err != nil {
logging.ErrorPersist(fmt.Sprintf("Failed to save session title: %v", err))
}
2025-03-27 22:35:48 +01:00
}
}
2025-04-13 14:37:05 +02:00
// TrackUsage updates token usage statistics for the session
func (a *agent) TrackUsage(ctx context.Context, sessionID string, model models.Model, usage provider.TokenUsage) error {
session, err := a.sessions.Get(ctx, sessionID)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-13 14:37:05 +02:00
return fmt.Errorf("failed to get session: %w", err)
2025-03-27 22:35:48 +01:00
}
cost := model.CostPer1MInCached/1e6*float64(usage.CacheCreationTokens) +
model.CostPer1MOutCached/1e6*float64(usage.CacheReadTokens) +
model.CostPer1MIn/1e6*float64(usage.InputTokens) +
model.CostPer1MOut/1e6*float64(usage.OutputTokens)
session.Cost += cost
session.CompletionTokens += usage.OutputTokens
session.PromptTokens += usage.InputTokens
2025-04-13 14:37:05 +02:00
_, err = a.sessions.Save(ctx, session)
if err != nil {
return fmt.Errorf("failed to save session: %w", err)
}
return nil
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
// processEvent handles different types of events during generation
func (a *agent) processEvent(
2025-04-13 13:17:17 +02:00
ctx context.Context,
2025-03-27 22:35:48 +01:00
sessionID string,
assistantMsg *message.Message,
event provider.ProviderEvent,
) error {
2025-04-13 14:37:05 +02:00
select {
case <-ctx.Done():
return ctx.Err()
default:
// Continue processing
}
2025-03-27 22:35:48 +01:00
switch event.Type {
case provider.EventThinkingDelta:
2025-04-03 15:20:15 +02:00
assistantMsg.AppendReasoningContent(event.Content)
2025-04-13 14:37:05 +02:00
return a.messages.Update(ctx, *assistantMsg)
2025-03-27 22:35:48 +01:00
case provider.EventContentDelta:
2025-04-03 15:20:15 +02:00
assistantMsg.AppendContent(event.Content)
2025-04-13 14:37:05 +02:00
return a.messages.Update(ctx, *assistantMsg)
2025-03-27 22:35:48 +01:00
case provider.EventError:
2025-04-10 13:29:44 +02:00
if errors.Is(event.Error, context.Canceled) {
2025-04-13 14:37:05 +02:00
logging.InfoPersist(fmt.Sprintf("Event processing canceled for session: %s", sessionID))
return context.Canceled
2025-04-10 13:29:44 +02:00
}
logging.ErrorPersist(event.Error.Error())
2025-03-27 22:35:48 +01:00
return event.Error
2025-04-07 19:43:31 +02:00
case provider.EventWarning:
2025-04-10 13:29:44 +02:00
logging.WarnPersist(event.Info)
2025-04-07 19:43:31 +02:00
case provider.EventInfo:
2025-04-10 13:29:44 +02:00
logging.InfoPersist(event.Info)
2025-03-27 22:35:48 +01:00
case provider.EventComplete:
2025-04-03 15:20:15 +02:00
assistantMsg.SetToolCalls(event.Response.ToolCalls)
assistantMsg.AddFinish(event.Response.FinishReason)
2025-04-13 14:37:05 +02:00
if err := a.messages.Update(ctx, *assistantMsg); err != nil {
return fmt.Errorf("failed to update message: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
return a.TrackUsage(ctx, sessionID, a.model, event.Response.Usage)
2025-03-27 22:35:48 +01:00
}
return nil
}
2025-04-13 14:37:05 +02:00
// ExecuteTools runs all tool calls sequentially and returns the results
func (a *agent) ExecuteTools(ctx context.Context, toolCalls []message.ToolCall, tls []tools.BaseTool) ([]message.ToolResult, error) {
2025-03-27 22:35:48 +01:00
toolResults := make([]message.ToolResult, len(toolCalls))
2025-04-10 13:29:44 +02:00
// Create a child context that can be canceled
ctx, cancel := context.WithCancel(ctx)
defer cancel()
2025-03-27 22:35:48 +01:00
2025-04-13 14:37:05 +02:00
// Check if already canceled before starting any execution
if ctx.Err() != nil {
// Mark all tools as canceled
for i, toolCall := range toolCalls {
toolResults[i] = message.ToolResult{
ToolCallID: toolCall.ID,
Content: "Tool execution canceled by user",
IsError: true,
}
}
return toolResults, ctx.Err()
}
2025-03-27 22:35:48 +01:00
2025-04-13 14:37:05 +02:00
for i, toolCall := range toolCalls {
// Check for cancellation before executing each tool
select {
case <-ctx.Done():
// Mark this and all remaining tools as canceled
for j := i; j < len(toolCalls); j++ {
toolResults[j] = message.ToolResult{
ToolCallID: toolCalls[j].ID,
Content: "Tool execution canceled by user",
2025-04-10 13:29:44 +02:00
IsError: true,
}
}
2025-04-13 14:37:05 +02:00
return toolResults, ctx.Err()
default:
// Continue processing
}
2025-04-10 13:29:44 +02:00
2025-04-13 14:37:05 +02:00
response := ""
isError := false
found := false
// Find and execute the appropriate tool
for _, tool := range tls {
if tool.Info().Name == toolCall.Name {
found = true
toolResult, toolErr := tool.Run(ctx, tools.ToolCall{
ID: toolCall.ID,
Name: toolCall.Name,
Input: toolCall.Input,
})
if toolErr != nil {
if errors.Is(toolErr, context.Canceled) {
response = "Tool execution canceled by user"
2025-03-27 22:35:48 +01:00
} else {
2025-04-13 14:37:05 +02:00
response = fmt.Sprintf("Error running tool: %s", toolErr)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
isError = true
} else {
response = toolResult.Content
isError = toolResult.IsError
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
break
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
}
2025-03-27 22:35:48 +01:00
2025-04-13 14:37:05 +02:00
if !found {
response = fmt.Sprintf("Tool not found: %s", toolCall.Name)
isError = true
}
2025-04-10 13:29:44 +02:00
2025-04-13 14:37:05 +02:00
toolResults[i] = message.ToolResult{
ToolCallID: toolCall.ID,
Content: response,
IsError: isError,
}
2025-04-10 13:29:44 +02:00
}
2025-03-27 22:35:48 +01:00
return toolResults, nil
}
2025-04-13 14:37:05 +02:00
// handleToolExecution processes tool calls and creates tool result messages
func (a *agent) handleToolExecution(
2025-03-27 22:35:48 +01:00
ctx context.Context,
assistantMsg message.Message,
) (*message.Message, error) {
2025-04-13 14:37:05 +02:00
select {
case <-ctx.Done():
// If cancelled, create tool results that indicate cancellation
if len(assistantMsg.ToolCalls()) > 0 {
toolResults := make([]message.ToolResult, 0, len(assistantMsg.ToolCalls()))
for _, tc := range assistantMsg.ToolCalls() {
toolResults = append(toolResults, message.ToolResult{
ToolCallID: tc.ID,
Content: "Tool execution canceled by user",
IsError: true,
})
}
// Use background context to ensure the message is created even if original context is cancelled
bgCtx := context.Background()
parts := make([]message.ContentPart, 0)
for _, toolResult := range toolResults {
parts = append(parts, toolResult)
}
msg, err := a.messages.Create(bgCtx, assistantMsg.SessionID, message.CreateMessageParams{
Role: message.Tool,
Parts: parts,
})
if err != nil {
return nil, fmt.Errorf("failed to create cancelled tool message: %w", err)
}
return &msg, ctx.Err()
}
return nil, ctx.Err()
default:
// Continue processing
}
2025-04-03 15:20:15 +02:00
if len(assistantMsg.ToolCalls()) == 0 {
2025-03-27 22:35:48 +01:00
return nil, nil
}
2025-04-13 14:37:05 +02:00
toolResults, err := a.ExecuteTools(ctx, assistantMsg.ToolCalls(), a.tools)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-13 14:37:05 +02:00
// If error is from cancellation, still return the partial results we have
if errors.Is(err, context.Canceled) {
// Use background context to ensure the message is created even if original context is cancelled
bgCtx := context.Background()
parts := make([]message.ContentPart, 0)
for _, toolResult := range toolResults {
parts = append(parts, toolResult)
}
msg, createErr := a.messages.Create(bgCtx, assistantMsg.SessionID, message.CreateMessageParams{
Role: message.Tool,
Parts: parts,
})
if createErr != nil {
logging.ErrorPersist(fmt.Sprintf("Failed to create tool message after cancellation: %v", createErr))
return nil, err
}
return &msg, err
}
2025-03-27 22:35:48 +01:00
return nil, err
2025-03-23 22:25:31 +01:00
}
2025-04-13 14:37:05 +02:00
parts := make([]message.ContentPart, 0, len(toolResults))
2025-04-03 15:20:15 +02:00
for _, toolResult := range toolResults {
parts = append(parts, toolResult)
}
2025-04-13 14:37:05 +02:00
msg, err := a.messages.Create(ctx, assistantMsg.SessionID, message.CreateMessageParams{
2025-04-03 15:20:15 +02:00
Role: message.Tool,
Parts: parts,
2025-03-27 22:35:48 +01:00
})
2025-04-13 14:37:05 +02:00
if err != nil {
return nil, fmt.Errorf("failed to create tool message: %w", err)
}
2025-03-27 22:35:48 +01:00
2025-04-13 14:37:05 +02:00
return &msg, nil
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
// generate handles the main generation workflow
func (a *agent) generate(ctx context.Context, sessionID string, content string) error {
2025-04-12 18:45:36 +02:00
ctx = context.WithValue(ctx, tools.SessionIDContextKey, sessionID)
2025-04-13 14:37:05 +02:00
// Handle context cancellation at any point
if err := ctx.Err(); err != nil {
return ErrRequestCancelled
}
messages, err := a.messages.List(ctx, sessionID)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-13 14:37:05 +02:00
return fmt.Errorf("failed to list messages: %w", err)
2025-03-27 22:35:48 +01:00
}
if len(messages) == 0 {
2025-04-13 14:37:05 +02:00
titleCtx := context.Background()
go a.handleTitleGeneration(titleCtx, sessionID, content)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
userMsg, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{
2025-04-03 15:20:15 +02:00
Role: message.User,
Parts: []message.ContentPart{
message.TextContent{
Text: content,
},
},
2025-03-27 22:35:48 +01:00
})
if err != nil {
2025-04-13 14:37:05 +02:00
return fmt.Errorf("failed to create user message: %w", err)
2025-03-27 22:35:48 +01:00
}
messages = append(messages, userMsg)
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
for {
2025-04-13 14:37:05 +02:00
// Check for cancellation before each iteration
2025-04-10 13:29:44 +02:00
select {
case <-ctx.Done():
2025-04-13 14:37:05 +02:00
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
default:
// Continue processing
}
2025-03-27 22:35:48 +01:00
2025-04-13 14:37:05 +02:00
eventChan, err := a.agent.StreamResponse(ctx, messages, a.tools)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-10 13:29:44 +02:00
if errors.Is(err, context.Canceled) {
2025-04-13 14:37:05 +02:00
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
}
2025-04-13 14:37:05 +02:00
return fmt.Errorf("failed to stream response: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
assistantMsg, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{
2025-04-03 15:20:15 +02:00
Role: message.Assistant,
Parts: []message.ContentPart{},
2025-04-13 14:37:05 +02:00
Model: a.model.ID,
2025-03-27 22:35:48 +01:00
})
if err != nil {
2025-04-13 14:37:05 +02:00
return fmt.Errorf("failed to create assistant message: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-12 18:45:36 +02:00
ctx = context.WithValue(ctx, tools.MessageIDContextKey, assistantMsg.ID)
2025-04-13 14:37:05 +02:00
// Process events from the LLM provider
2025-03-27 22:35:48 +01:00
for event := range eventChan {
2025-04-13 14:37:05 +02:00
if err := a.processEvent(ctx, sessionID, &assistantMsg, event); err != nil {
2025-04-10 13:29:44 +02:00
if errors.Is(err, context.Canceled) {
2025-04-13 14:37:05 +02:00
// Mark as canceled but don't create separate message
2025-04-10 13:29:44 +02:00
assistantMsg.AddFinish("canceled")
2025-04-13 14:37:05 +02:00
_ = a.messages.Update(context.Background(), assistantMsg)
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
}
2025-04-03 15:20:15 +02:00
assistantMsg.AddFinish("error:" + err.Error())
2025-04-13 14:37:05 +02:00
_ = a.messages.Update(ctx, assistantMsg)
return fmt.Errorf("event processing error: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-10 13:29:44 +02:00
2025-04-13 14:37:05 +02:00
// Check for cancellation during event processing
2025-04-10 13:29:44 +02:00
select {
case <-ctx.Done():
2025-04-13 14:37:05 +02:00
// Mark as canceled
2025-04-10 13:29:44 +02:00
assistantMsg.AddFinish("canceled")
2025-04-13 14:37:05 +02:00
_ = a.messages.Update(context.Background(), assistantMsg)
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
default:
}
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
// Check for cancellation before tool execution
2025-04-10 13:29:44 +02:00
select {
case <-ctx.Done():
2025-04-13 14:37:05 +02:00
assistantMsg.AddFinish("canceled_by_user")
_ = a.messages.Update(context.Background(), assistantMsg)
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
default:
}
2025-04-03 15:20:15 +02:00
2025-04-13 14:37:05 +02:00
// Execute any tool calls
toolMsg, err := a.handleToolExecution(ctx, assistantMsg)
2025-03-27 22:35:48 +01:00
if err != nil {
2025-04-10 13:29:44 +02:00
if errors.Is(err, context.Canceled) {
2025-04-13 14:37:05 +02:00
assistantMsg.AddFinish("canceled_by_user")
_ = a.messages.Update(context.Background(), assistantMsg)
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
}
2025-04-13 14:37:05 +02:00
return fmt.Errorf("tool execution error: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
if err := a.messages.Update(ctx, assistantMsg); err != nil {
return fmt.Errorf("failed to update assistant message: %w", err)
}
2025-04-10 13:29:44 +02:00
2025-04-13 14:37:05 +02:00
// If no tool calls, we're done
2025-04-03 15:20:15 +02:00
if len(assistantMsg.ToolCalls()) == 0 {
2025-03-27 22:35:48 +01:00
break
}
2025-04-13 14:37:05 +02:00
// Add messages for next iteration
2025-03-27 22:35:48 +01:00
messages = append(messages, assistantMsg)
2025-04-13 14:37:05 +02:00
if toolMsg != nil {
messages = append(messages, *toolMsg)
2025-03-27 22:35:48 +01:00
}
2025-04-10 13:29:44 +02:00
2025-04-13 14:37:05 +02:00
// Check for cancellation after tool execution
2025-04-10 13:29:44 +02:00
select {
case <-ctx.Done():
2025-04-13 14:37:05 +02:00
return ErrRequestCancelled
2025-04-10 13:29:44 +02:00
default:
}
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
return nil
}
2025-04-13 14:37:05 +02:00
// getAgentProviders initializes the LLM providers based on the chosen model
2025-03-27 22:35:48 +01:00
func getAgentProviders(ctx context.Context, model models.Model) (provider.Provider, provider.Provider, error) {
maxTokens := config.Get().Model.CoderMaxTokens
providerConfig, ok := config.Get().Providers[model.Provider]
2025-04-13 13:17:17 +02:00
if !ok || providerConfig.Disabled {
2025-04-13 14:37:05 +02:00
return nil, nil, ErrProviderNotEnabled
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
var agentProvider provider.Provider
var titleGenerator provider.Provider
2025-04-13 14:37:05 +02:00
var err error
2025-03-27 22:35:48 +01:00
switch model.Provider {
case models.ProviderOpenAI:
agentProvider, err = provider.NewOpenAIProvider(
provider.WithOpenAISystemMessage(
prompt.CoderOpenAISystemPrompt(),
),
provider.WithOpenAIMaxTokens(maxTokens),
provider.WithOpenAIModel(model),
provider.WithOpenAIKey(providerConfig.APIKey),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create OpenAI agent provider: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
titleGenerator, err = provider.NewOpenAIProvider(
provider.WithOpenAISystemMessage(
prompt.TitlePrompt(),
),
provider.WithOpenAIMaxTokens(80),
provider.WithOpenAIModel(model),
provider.WithOpenAIKey(providerConfig.APIKey),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create OpenAI title generator: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
case models.ProviderAnthropic:
agentProvider, err = provider.NewAnthropicProvider(
provider.WithAnthropicSystemMessage(
prompt.CoderAnthropicSystemPrompt(),
),
provider.WithAnthropicMaxTokens(maxTokens),
provider.WithAnthropicKey(providerConfig.APIKey),
provider.WithAnthropicModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Anthropic agent provider: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
titleGenerator, err = provider.NewAnthropicProvider(
provider.WithAnthropicSystemMessage(
prompt.TitlePrompt(),
),
provider.WithAnthropicMaxTokens(80),
provider.WithAnthropicKey(providerConfig.APIKey),
provider.WithAnthropicModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Anthropic title generator: %w", err)
2025-03-27 22:35:48 +01:00
}
case models.ProviderGemini:
agentProvider, err = provider.NewGeminiProvider(
ctx,
provider.WithGeminiSystemMessage(
prompt.CoderOpenAISystemPrompt(),
),
provider.WithGeminiMaxTokens(int32(maxTokens)),
provider.WithGeminiKey(providerConfig.APIKey),
provider.WithGeminiModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Gemini agent provider: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
titleGenerator, err = provider.NewGeminiProvider(
ctx,
provider.WithGeminiSystemMessage(
prompt.TitlePrompt(),
),
provider.WithGeminiMaxTokens(80),
provider.WithGeminiKey(providerConfig.APIKey),
provider.WithGeminiModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Gemini title generator: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
case models.ProviderGROQ:
agentProvider, err = provider.NewOpenAIProvider(
provider.WithOpenAISystemMessage(
prompt.CoderAnthropicSystemPrompt(),
),
provider.WithOpenAIMaxTokens(maxTokens),
provider.WithOpenAIModel(model),
provider.WithOpenAIKey(providerConfig.APIKey),
provider.WithOpenAIBaseURL("https://api.groq.com/openai/v1"),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create GROQ agent provider: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-13 14:37:05 +02:00
2025-03-27 22:35:48 +01:00
titleGenerator, err = provider.NewOpenAIProvider(
provider.WithOpenAISystemMessage(
prompt.TitlePrompt(),
),
provider.WithOpenAIMaxTokens(80),
provider.WithOpenAIModel(model),
provider.WithOpenAIKey(providerConfig.APIKey),
provider.WithOpenAIBaseURL("https://api.groq.com/openai/v1"),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create GROQ title generator: %w", err)
2025-03-27 22:35:48 +01:00
}
2025-04-09 17:45:41 +02:00
case models.ProviderBedrock:
agentProvider, err = provider.NewBedrockProvider(
provider.WithBedrockSystemMessage(
prompt.CoderAnthropicSystemPrompt(),
),
provider.WithBedrockMaxTokens(maxTokens),
provider.WithBedrockModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Bedrock agent provider: %w", err)
2025-04-09 17:45:41 +02:00
}
2025-04-13 14:37:05 +02:00
2025-04-09 17:45:41 +02:00
titleGenerator, err = provider.NewBedrockProvider(
provider.WithBedrockSystemMessage(
prompt.TitlePrompt(),
),
2025-04-13 14:37:05 +02:00
provider.WithBedrockMaxTokens(80),
2025-04-09 17:45:41 +02:00
provider.WithBedrockModel(model),
)
if err != nil {
2025-04-13 14:37:05 +02:00
return nil, nil, fmt.Errorf("failed to create Bedrock title generator: %w", err)
2025-04-09 17:45:41 +02:00
}
2025-04-13 14:37:05 +02:00
default:
return nil, nil, fmt.Errorf("unsupported provider: %s", model.Provider)
2025-03-27 22:35:48 +01:00
}
return agentProvider, titleGenerator, nil
2025-03-23 22:25:31 +01:00
}