Files
Local_Perplexity/internal/model/responses.go
fedos 8e74e53b3d feat: add Ollama proxy with LLM router and Codex CLI support
Go-сервис-прокси между Codex CLI и Ollama. Добавляет Bearer-авторизацию,
LLM-маршрутизатор (deepseek классифицирует запросы: code/doc/general),
поддержку OpenAI Responses API для Codex CLI, стриминг SSE, кеш модели.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 15:25:15 +03:00

108 lines
2.9 KiB
Go

package model
import (
"encoding/json"
"log"
)
// ResponsesRequest — запрос к OpenAI Responses API (/responses или /v1/responses)
type ResponsesRequest struct {
Model string `json:"model"`
Input json.RawMessage `json:"input"` // string или []ResponsesInputMessage
Instructions string `json:"instructions,omitempty"`
Stream *bool `json:"stream,omitempty"`
}
// ResponsesInputMessage — сообщение в формате Responses API
// content может быть строкой или массивом [{type: "input_text", text: "..."}]
type ResponsesInputMessage struct {
Type string `json:"type,omitempty"` // "message"
Role string `json:"role"`
Content json.RawMessage `json:"content"`
}
// ContentPart — элемент массива content в Responses API
type ContentPart struct {
Type string `json:"type"` // "input_text", "output_text", etc.
Text string `json:"text"`
}
// ToMessages конвертирует Input + Instructions в []Message для Ollama
func (r *ResponsesRequest) ToMessages() []Message {
var messages []Message
// Instructions → system message
if r.Instructions != "" {
messages = append(messages, Message{Role: "system", Content: r.Instructions})
}
if len(r.Input) == 0 {
return messages
}
log.Printf("responses: raw input: %s", string(r.Input))
// Попробовать как строку
var inputStr string
if err := json.Unmarshal(r.Input, &inputStr); err == nil {
messages = append(messages, Message{Role: "user", Content: inputStr})
return messages
}
// Попробовать как массив ResponsesInputMessage (формат Codex CLI)
var inputMsgs []ResponsesInputMessage
if err := json.Unmarshal(r.Input, &inputMsgs); err == nil {
for _, msg := range inputMsgs {
content := extractContent(msg.Content)
role := msg.Role
if role == "" {
role = "user"
}
if content != "" {
messages = append(messages, Message{Role: role, Content: content})
}
}
if len(messages) > 0 {
return messages
}
}
// Попробовать как простой массив Message (совместимость)
var simpleMsgs []Message
if err := json.Unmarshal(r.Input, &simpleMsgs); err == nil {
messages = append(messages, simpleMsgs...)
}
return messages
}
// extractContent извлекает текст из content, которое может быть строкой или массивом ContentPart
func extractContent(raw json.RawMessage) string {
if len(raw) == 0 {
return ""
}
// Как строку
var s string
if err := json.Unmarshal(raw, &s); err == nil {
return s
}
// Как массив ContentPart
var parts []ContentPart
if err := json.Unmarshal(raw, &parts); err == nil {
var text string
for _, p := range parts {
if p.Text != "" {
if text != "" {
text += "\n"
}
text += p.Text
}
}
return text
}
return ""
}