- Add request/response converters (OpenAI <-> Anthropic formats) - Implement SSE streaming conversion (Anthropic events -> OpenAI SSE) - Add /v1/models endpoint with Claude model list - Add /v1/chat/completions endpoint with streaming and non-streaming support - Fix context key type matching bug (sessionIDKey) - Configurable upstream URL via config.yaml - Mimic claude-code CLI headers for upstream requests
238 lines
7 KiB
Go
238 lines
7 KiB
Go
package main
|
|
|
|
import "encoding/json"
|
|
|
|
// OpenAI Types
|
|
|
|
type ChatCompletionRequest struct {
|
|
Model string `json:"model"`
|
|
Messages []Message `json:"messages"`
|
|
Temperature *float64 `json:"temperature,omitempty"`
|
|
TopP *float64 `json:"top_p,omitempty"`
|
|
MaxTokens *int `json:"max_tokens,omitempty"`
|
|
Stream *bool `json:"stream,omitempty"`
|
|
Stop []string `json:"stop,omitempty"`
|
|
Tools []Tool `json:"tools,omitempty"`
|
|
ToolChoices *ToolChoice `json:"tool_choice,omitempty"`
|
|
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
|
|
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
|
User string `json:"user,omitempty"`
|
|
}
|
|
|
|
type Message struct {
|
|
Role string `json:"role"`
|
|
Content interface{} `json:"content"` // string or []ContentPart
|
|
Name string `json:"name,omitempty"`
|
|
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
|
}
|
|
|
|
type ContentPart struct {
|
|
Type string `json:"type"`
|
|
Text string `json:"text,omitempty"`
|
|
Source *ImageSource `json:"source,omitempty"`
|
|
}
|
|
|
|
type ImageSource struct {
|
|
Type string `json:"type"`
|
|
MediaURL string `json:"media_url,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
}
|
|
|
|
type Tool struct {
|
|
Type string `json:"type"`
|
|
Function Function `json:"function"`
|
|
}
|
|
|
|
type Function struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
|
}
|
|
|
|
type ToolChoice struct {
|
|
Type string `json:"type,omitempty"` // "auto", "none", "required"
|
|
Function *FunctionRef `json:"function,omitempty"`
|
|
}
|
|
|
|
type FunctionRef struct {
|
|
Name string `json:"name,omitempty"`
|
|
}
|
|
|
|
type Choice struct {
|
|
Index int `json:"index"`
|
|
Message Message `json:"message"`
|
|
FinishReason string `json:"finish_reason,omitempty"`
|
|
}
|
|
|
|
type ChatCompletionResponse struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Created int64 `json:"created"`
|
|
Model string `json:"model"`
|
|
Choices []Choice `json:"choices"`
|
|
Usage Usage `json:"usage"`
|
|
}
|
|
|
|
type Usage struct {
|
|
PromptTokens int `json:"prompt_tokens"`
|
|
CompletionTokens int `json:"completion_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
}
|
|
|
|
type ToolCall struct {
|
|
Index int `json:"index,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Function FunctionCall `json:"function,omitempty"`
|
|
}
|
|
|
|
type FunctionCall struct {
|
|
Name string `json:"name,omitempty"`
|
|
Arguments string `json:"arguments,omitempty"`
|
|
}
|
|
|
|
// Anthropic Types
|
|
|
|
type AnthropicRequest struct {
|
|
Model string `json:"model"`
|
|
Messages []AnthropicMessage `json:"messages"`
|
|
System string `json:"system,omitempty"`
|
|
MaxTokens int `json:"max_tokens"`
|
|
Stream bool `json:"stream,omitempty"`
|
|
Temperature *float64 `json:"temperature,omitempty"`
|
|
TopP *float64 `json:"top_p,omitempty"`
|
|
StopSequences []string `json:"stop_sequences,omitempty"`
|
|
Tools []AnthropicTool `json:"tools,omitempty"`
|
|
ToolChoice *AnthropicToolChoice `json:"tool_choice,omitempty"`
|
|
}
|
|
|
|
type AnthropicMessage struct {
|
|
Role string `json:"role"`
|
|
Content interface{} `json:"content"` // string or []ContentBlock
|
|
}
|
|
|
|
type ContentBlock struct {
|
|
Type string `json:"type,omitempty"` // "text", "tool_use", "tool_result"
|
|
Text string `json:"text,omitempty"`
|
|
Id string `json:"id,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Input map[string]interface{} `json:"input,omitempty"`
|
|
ToolUseId string `json:"tool_use_id,omitempty"`
|
|
Content string `json:"content,omitempty"`
|
|
}
|
|
|
|
type AnthropicTool struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
InputSchema map[string]interface{} `json:"input_schema,omitempty"`
|
|
}
|
|
|
|
type AnthropicToolChoice struct {
|
|
Type string `json:"type,omitempty"` // "auto", "none", "any"
|
|
Name string `json:"name,omitempty"`
|
|
}
|
|
|
|
type AnthropicResponse struct {
|
|
Type string `json:"type,omitempty"`
|
|
Id string `json:"id"`
|
|
Role string `json:"role,omitempty"`
|
|
Model string `json:"model,omitempty"`
|
|
Content []ContentBlock `json:"content,omitempty"`
|
|
StopReason string `json:"stop_reason,omitempty"`
|
|
StopSequence string `json:"stop_sequence,omitempty"`
|
|
Usage AnthropicUsage `json:"usage,omitempty"`
|
|
}
|
|
|
|
type AnthropicUsage struct {
|
|
InputTokens int `json:"input_tokens,omitempty"`
|
|
OutputTokens int `json:"output_tokens,omitempty"`
|
|
}
|
|
|
|
// Streaming Types
|
|
|
|
type StreamChunk struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Created int64 `json:"created"`
|
|
Model string `json:"model"`
|
|
Choices []StreamChoice `json:"choices"`
|
|
}
|
|
|
|
type StreamChoice struct {
|
|
Index int `json:"index"`
|
|
Delta Delta `json:"delta"`
|
|
FinishReason string `json:"finish_reason,omitempty"`
|
|
}
|
|
|
|
type Delta struct {
|
|
Role string `json:"role,omitempty"`
|
|
Content string `json:"content,omitempty"`
|
|
ToolCalls []StreamToolCall `json:"tool_calls,omitempty"`
|
|
}
|
|
|
|
type StreamToolCall struct {
|
|
Index int `json:"index,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Function StreamFunction `json:"function,omitempty"`
|
|
}
|
|
|
|
type StreamFunction struct {
|
|
Name string `json:"name,omitempty"`
|
|
Arguments string `json:"arguments,omitempty"`
|
|
}
|
|
|
|
|
|
|
|
// SSE Event Types
|
|
|
|
type SSEMessage struct {
|
|
Type string `json:"type"`
|
|
Data json.RawMessage `json:"data,omitempty"`
|
|
}
|
|
|
|
type AnthropicMessageStart struct {
|
|
Type string `json:"type"`
|
|
Message struct {
|
|
Id string `json:"id"`
|
|
Role string `json:"role"`
|
|
Type string `json:"type"`
|
|
} `json:"message"`
|
|
}
|
|
|
|
type AnthropicBlockStart struct {
|
|
Type string `json:"type"`
|
|
Index int `json:"index"`
|
|
ContentBlock struct {
|
|
Type string `json:"type"`
|
|
Id string `json:"id"`
|
|
Name string `json:"name,omitempty"`
|
|
} `json:"content_block"`
|
|
}
|
|
|
|
type AnthropicDelta struct {
|
|
Type string `json:"type"`
|
|
Index int `json:"index"`
|
|
Delta struct {
|
|
Text string `json:"text,omitempty"`
|
|
InputJSONDelta string `json:"input_json_delta,omitempty"`
|
|
} `json:"delta"`
|
|
}
|
|
|
|
type AnthropicBlockStop struct {
|
|
Type string `json:"type"`
|
|
Index int `json:"index"`
|
|
}
|
|
|
|
type AnthropicMessageDelta struct {
|
|
Type string `json:"type"`
|
|
Delta struct {
|
|
StopSequence string `json:"stop_sequence,omitempty"`
|
|
} `json:"delta"`
|
|
Usage *AnthropicUsage `json:"usage,omitempty"`
|
|
StopReason string `json:"stop_reason,omitempty"`
|
|
}
|
|
|
|
type AnthropicMessageStop struct {
|
|
Type string `json:"type"`
|
|
}
|