Files
GopherGate/internal/models/models.go
hobokenchicken 6b10d4249c
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled
feat: migrate backend from rust to go
This commit replaces the Axum/Rust backend with a Gin/Go implementation. The original Rust code has been archived in the 'rust' branch.
2026-03-19 10:30:05 -04:00

217 lines
6.1 KiB
Go

package models
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
)
// OpenAI-compatible Request/Response Structs
type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *uint32 `json:"top_k,omitempty"`
N *uint32 `json:"n,omitempty"`
Stop json.RawMessage `json:"stop,omitempty"` // Can be string or array of strings
MaxTokens *uint32 `json:"max_tokens,omitempty"`
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
Stream *bool `json:"stream,omitempty"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
}
type ChatMessage struct {
Role string `json:"role"` // "system", "user", "assistant", "tool"
Content interface{} `json:"content"`
ReasoningContent *string `json:"reasoning_content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
Name *string `json:"name,omitempty"`
ToolCallID *string `json:"tool_call_id,omitempty"`
}
type ContentPart struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
ImageUrl *ImageUrl `json:"image_url,omitempty"`
}
type ImageUrl struct {
URL string `json:"url"`
Detail *string `json:"detail,omitempty"`
}
// Tool-Calling Types
type Tool struct {
Type string `json:"type"`
Function FunctionDef `json:"function"`
}
type FunctionDef struct {
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Parameters json.RawMessage `json:"parameters,omitempty"`
}
type ToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function FunctionCall `json:"function"`
}
type FunctionCall struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
type ToolCallDelta struct {
Index uint32 `json:"index"`
ID *string `json:"id,omitempty"`
Type *string `json:"type,omitempty"`
Function *FunctionCallDelta `json:"function,omitempty"`
}
type FunctionCallDelta struct {
Name *string `json:"name,omitempty"`
Arguments *string `json:"arguments,omitempty"`
}
// OpenAI-compatible Response Structs
type ChatCompletionResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []ChatChoice `json:"choices"`
Usage *Usage `json:"usage,omitempty"`
}
type ChatChoice struct {
Index uint32 `json:"index"`
Message ChatMessage `json:"message"`
FinishReason *string `json:"finish_reason,omitempty"`
}
type Usage struct {
PromptTokens uint32 `json:"prompt_tokens"`
CompletionTokens uint32 `json:"completion_tokens"`
TotalTokens uint32 `json:"total_tokens"`
ReasoningTokens *uint32 `json:"reasoning_tokens,omitempty"`
CacheReadTokens *uint32 `json:"cache_read_tokens,omitempty"`
CacheWriteTokens *uint32 `json:"cache_write_tokens,omitempty"`
}
// Streaming Response Structs
type ChatCompletionStreamResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []ChatStreamChoice `json:"choices"`
Usage *Usage `json:"usage,omitempty"`
}
type ChatStreamChoice struct {
Index uint32 `json:"index"`
Delta ChatStreamDelta `json:"delta"`
FinishReason *string `json:"finish_reason,omitempty"`
}
type ChatStreamDelta struct {
Role *string `json:"role,omitempty"`
Content *string `json:"content,omitempty"`
ReasoningContent *string `json:"reasoning_content,omitempty"`
ToolCalls []ToolCallDelta `json:"tool_calls,omitempty"`
}
type StreamUsage struct {
PromptTokens uint32 `json:"prompt_tokens"`
CompletionTokens uint32 `json:"completion_tokens"`
TotalTokens uint32 `json:"total_tokens"`
ReasoningTokens uint32 `json:"reasoning_tokens"`
CacheReadTokens uint32 `json:"cache_read_tokens"`
CacheWriteTokens uint32 `json:"cache_write_tokens"`
}
// Unified Request Format (for internal use)
type UnifiedRequest struct {
ClientID string
Model string
Messages []UnifiedMessage
Temperature *float64
TopP *float64
TopK *uint32
N *uint32
Stop []string
MaxTokens *uint32
PresencePenalty *float64
FrequencyPenalty *float64
Stream bool
HasImages bool
Tools []Tool
ToolChoice json.RawMessage
}
type UnifiedMessage struct {
Role string
Content []UnifiedContentPart
ReasoningContent *string
ToolCalls []ToolCall
Name *string
ToolCallID *string
}
type UnifiedContentPart struct {
Type string
Text string
Image *ImageInput
}
type ImageInput struct {
Base64 string `json:"base64,omitempty"`
URL string `json:"url,omitempty"`
MimeType string `json:"mime_type,omitempty"`
}
func (i *ImageInput) ToBase64() (string, string, error) {
if i.Base64 != "" {
return i.Base64, i.MimeType, nil
}
if i.URL != "" {
client := resty.New()
resp, err := client.R().Get(i.URL)
if err != nil {
return "", "", fmt.Errorf("failed to fetch image: %w", err)
}
if !resp.IsSuccess() {
return "", "", fmt.Errorf("failed to fetch image: HTTP %d", resp.StatusCode())
}
mimeType := resp.Header().Get("Content-Type")
if mimeType == "" {
mimeType = "image/jpeg"
}
encoded := base64.StdEncoding.EncodeToString(resp.Body())
return encoded, mimeType, nil
}
return "", "", fmt.Errorf("empty image input")
}
// AuthInfo for context
type AuthInfo struct {
Token string
ClientID string
}