Files
goclaw/internal/providers/codex_build.go
T
viettranx bdb60de7ae chore: upgrade Go 1.25 → 1.26 and apply go fix modernizations
- Update go.mod and Dockerfile to Go 1.26
- Apply `go fix ./...` stdlib modernizations across 170+ files
- Add `go fix` to post-implementation checklist in CLAUDE.md
- Fix go fix misapplied rewrite in loop_history.go
2026-03-10 00:09:15 +07:00

180 lines
4.2 KiB
Go

package providers
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
// buildRequestBody converts internal ChatRequest to Responses API format.
func (p *CodexProvider) buildRequestBody(req ChatRequest, stream bool) map[string]any {
model := req.Model
if model == "" {
model = p.defaultModel
}
var instructions string
var input []any
for _, m := range req.Messages {
switch m.Role {
case "system":
if instructions == "" {
instructions = m.Content
} else {
instructions += "\n\n" + m.Content
}
case "user":
if len(m.Images) > 0 {
var parts []map[string]any
for _, img := range m.Images {
parts = append(parts, map[string]any{
"type": "input_image",
"image_url": fmt.Sprintf("data:%s;base64,%s", img.MimeType, img.Data),
})
}
if m.Content != "" {
parts = append(parts, map[string]any{
"type": "input_text",
"text": m.Content,
})
}
input = append(input, map[string]any{
"role": "user",
"content": parts,
})
} else {
input = append(input, map[string]any{
"role": "user",
"content": m.Content,
})
}
case "assistant":
if len(m.ToolCalls) > 0 {
for _, tc := range m.ToolCalls {
argsJSON, _ := json.Marshal(tc.Arguments)
callID := toFcID(tc.ID)
input = append(input, map[string]any{
"type": "function_call",
"id": callID,
"call_id": callID,
"name": tc.Name,
"arguments": string(argsJSON),
})
}
}
if m.Content != "" {
item := map[string]any{
"type": "message",
"role": "assistant",
"content": []map[string]any{
{"type": "output_text", "text": m.Content},
},
}
if m.Phase != "" {
item["phase"] = m.Phase
}
input = append(input, item)
}
case "tool":
input = append(input, map[string]any{
"type": "function_call_output",
"call_id": toFcID(m.ToolCallID),
"output": m.Content,
})
}
}
body := map[string]any{
"model": model,
"input": input,
"stream": stream,
"store": false,
}
if instructions == "" {
instructions = "You are a helpful assistant."
}
body["instructions"] = instructions
if len(req.Tools) > 0 {
var tools []map[string]any
for _, t := range req.Tools {
tools = append(tools, map[string]any{
"type": "function",
"name": t.Function.Name,
"description": t.Function.Description,
"parameters": t.Function.Parameters,
})
}
body["tools"] = tools
}
if level, ok := req.Options[OptThinkingLevel].(string); ok && level != "" && level != "off" {
body["reasoning"] = map[string]any{"effort": level}
}
return body
}
func (p *CodexProvider) doRequest(ctx context.Context, body any) (io.ReadCloser, error) {
data, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("%s: marshal request: %w", p.name, err)
}
endpoint := p.apiBase + "/codex/responses"
httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("%s: create request: %w", p.name, err)
}
httpReq.Header.Set("Content-Type", "application/json")
token, err := p.tokenSource.Token()
if err != nil {
return nil, fmt.Errorf("%s: get auth token: %w", p.name, err)
}
httpReq.Header.Set("Authorization", "Bearer "+token)
httpReq.Header.Set("OpenAI-Beta", "responses=v1")
resp, err := p.client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("%s: request failed: %w", p.name, err)
}
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
resp.Body.Close()
retryAfter := ParseRetryAfter(resp.Header.Get("Retry-After"))
return nil, &HTTPError{
Status: resp.StatusCode,
Body: fmt.Sprintf("%s: %s", p.name, string(respBody)),
RetryAfter: retryAfter,
}
}
return resp.Body, nil
}
// toFcID ensures a tool call ID starts with "fc_" as required by the Responses API.
func toFcID(id string) string {
if strings.HasPrefix(id, "fc_") {
return id
}
if strings.HasPrefix(id, "tool_") {
return "fc_" + id[len("tool_"):]
}
if strings.HasPrefix(id, "call_") {
return "fc_" + id[len("call_"):]
}
return "fc_" + id
}