diff --git a/pkg/domain/entity/account.go b/pkg/domain/entity/account.go new file mode 100644 index 0000000..3b88baa --- /dev/null +++ b/pkg/domain/entity/account.go @@ -0,0 +1,22 @@ +package entity + +// Account represents an account in the pool +type Account struct { + ConfigDir string + ActiveRequests int + LastUsed int64 + RateLimitUntil int64 +} + +// AccountStat represents account statistics +type AccountStat struct { + ConfigDir string + ActiveRequests int + TotalRequests int + TotalSuccess int + TotalErrors int + TotalRateLimits int + TotalLatencyMs int64 + IsRateLimited bool + RateLimitUntil int64 +} diff --git a/pkg/domain/entity/chunk.go b/pkg/domain/entity/chunk.go new file mode 100644 index 0000000..5726c9d --- /dev/null +++ b/pkg/domain/entity/chunk.go @@ -0,0 +1,20 @@ +package entity + +// ChunkType represents the type of stream chunk +type ChunkType int + +const ( + ChunkText ChunkType = iota + ChunkThinking + ChunkToolCall + ChunkDone +) + +// StreamChunk represents a chunk in SSE streaming +type StreamChunk struct { + Type ChunkType + Text string + Thinking string + ToolCall *ToolCall + Done bool +} diff --git a/pkg/domain/entity/message.go b/pkg/domain/entity/message.go new file mode 100644 index 0000000..d1d25f0 --- /dev/null +++ b/pkg/domain/entity/message.go @@ -0,0 +1,33 @@ +package entity + +// Message represents a chat message +type Message struct { + Role string + Content interface{} +} + +// Tool represents a tool definition +type Tool struct { + Type string + Function ToolFunction +} + +// ToolFunction represents a tool function definition +type ToolFunction struct { + Name string + Description string + Parameters interface{} +} + +// ToolCall represents a tool call result +type ToolCall struct { + ID string + Name string + Arguments string +} + +// FunctionCall represents a function call +type FunctionCall struct { + Name string + Arguments string +} diff --git a/pkg/domain/repository/account.go b/pkg/domain/repository/account.go new file mode 100644 index 0000000..1850e48 --- /dev/null +++ b/pkg/domain/repository/account.go @@ -0,0 +1,27 @@ +package repository + +import ( + "context" + + "cursor-api-proxy/pkg/domain/entity" +) + +// AccountPool defines the interface for account pool management +type AccountPool interface { + GetNextConfigDir() string + ReportRequestStart(configDir string) + ReportRequestEnd(configDir string) + ReportRequestSuccess(configDir string, latencyMs int64) + ReportRequestError(configDir string, latencyMs int64) + ReportRateLimit(configDir string, penaltyMs int64) + GetStats() []entity.AccountStat + Count() int +} + +// Provider defines the interface for AI providers +type Provider interface { + Name() string + Generate(ctx context.Context, model string, messages []entity.Message, + tools []entity.Tool, callback func(entity.StreamChunk)) error + Close() error +} diff --git a/pkg/domain/types/errors.go b/pkg/domain/types/errors.go new file mode 100644 index 0000000..c3951d7 --- /dev/null +++ b/pkg/domain/types/errors.go @@ -0,0 +1,13 @@ +package types + +import "errors" + +var ( + ErrInvalidRequest = errors.New("invalid request") + ErrProviderNotFound = errors.New("provider not found") + ErrAccountExhausted = errors.New("all accounts exhausted") + ErrRateLimited = errors.New("rate limited") + ErrTimeout = errors.New("request timeout") + ErrClientDisconnect = errors.New("client disconnected") + ErrAgentError = errors.New("agent execution error") +) \ No newline at end of file diff --git a/pkg/domain/types/models.go b/pkg/domain/types/models.go new file mode 100644 index 0000000..8842664 --- /dev/null +++ b/pkg/domain/types/models.go @@ -0,0 +1,30 @@ +package types + +// Model mappings for Cursor API +var AnthropicToCursor = map[string]string{ + "claude-3-5-sonnet": "claude-3.5-sonnet", + "claude-3-5-sonnet-20241022": "claude-3.5-sonnet", + "claude-3-5-haiku": "claude-3.5-haiku", + "claude-3-opus": "claude-3-opus", + "claude-3-sonnet": "claude-3-sonnet", + "claude-3-haiku": "claude-3-haiku", +} + +// Cursor model aliases +var CursorModelAliases = []string{ + "auto", + "claude-3.5-sonnet", + "claude-3.5-haiku", + "claude-3-opus", + "gpt-4", + "gpt-4o", + "gemini-2.0-flash", +} + +// ResolveToCursorModel resolves a model name to Cursor model +func ResolveToCursorModel(model string) string { + if mapped, ok := AnthropicToCursor[model]; ok { + return mapped + } + return model +} \ No newline at end of file diff --git a/pkg/domain/usecase/chat.go b/pkg/domain/usecase/chat.go new file mode 100644 index 0000000..3fbc138 --- /dev/null +++ b/pkg/domain/usecase/chat.go @@ -0,0 +1,47 @@ +package usecase + +import ( + "context" + + "cursor-api-proxy/pkg/domain/entity" +) + +// ChatUsecase defines the interface for chat operations +type ChatUsecase interface { + Execute(ctx context.Context, input ChatInput) (ChatOutput, error) + Stream(ctx context.Context, input ChatInput, callback func(entity.StreamChunk)) error +} + +// ChatInput represents the input for chat operations +type ChatInput struct { + Model string + Messages []entity.Message + Tools []entity.Tool + Stream bool +} + +// ChatOutput represents the output from chat operations +type ChatOutput struct { + Content string + Thinking string + ToolCalls []entity.ToolCall +} + +// AgentRunner defines the interface for running AI agents +type AgentRunner interface { + RunSync(ctx context.Context, config interface{}, args []string) (RunResult, error) + RunStream(ctx context.Context, config interface{}, args []string, onLine func(string)) (StreamResult, error) +} + +// RunResult represents the result of a synchronous agent run +type RunResult struct { + Code int + Stdout string + Stderr string +} + +// StreamResult represents the result of a streaming agent run +type StreamResult struct { + Code int + Stderr string +}