mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-12 18:11:12 +00:00
bdb60de7ae
- 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
81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
package tools
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/providers"
|
|
)
|
|
|
|
// callOpenAICompatJSON sends a raw JSON body to an OpenAI-compatible chat/completions endpoint
|
|
// and parses the response into a ChatResponse. Used by audio/video tools that need custom
|
|
// message formats not supported by the generic providers.ChatRequest interface.
|
|
func callOpenAICompatJSON(ctx context.Context, apiKey, baseURL string, body map[string]any, timeout time.Duration) (*providers.ChatResponse, error) {
|
|
jsonBody, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal request: %w", err)
|
|
}
|
|
|
|
url := strings.TrimRight(baseURL, "/") + "/chat/completions"
|
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+apiKey)
|
|
|
|
client := &http.Client{Timeout: timeout}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("HTTP request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read response: %w", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, truncateStr(string(respBody), 500))
|
|
}
|
|
|
|
// Parse standard OpenAI chat completion response.
|
|
var oaiResp struct {
|
|
Choices []struct {
|
|
Message struct {
|
|
Content string `json:"content"`
|
|
} `json:"message"`
|
|
} `json:"choices"`
|
|
Usage *struct {
|
|
PromptTokens int `json:"prompt_tokens"`
|
|
CompletionTokens int `json:"completion_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
} `json:"usage"`
|
|
}
|
|
if err := json.Unmarshal(respBody, &oaiResp); err != nil {
|
|
return nil, fmt.Errorf("parse response: %w", err)
|
|
}
|
|
if len(oaiResp.Choices) == 0 || oaiResp.Choices[0].Message.Content == "" {
|
|
return nil, fmt.Errorf("empty response")
|
|
}
|
|
|
|
chatResp := &providers.ChatResponse{
|
|
Content: oaiResp.Choices[0].Message.Content,
|
|
FinishReason: "stop",
|
|
}
|
|
if oaiResp.Usage != nil {
|
|
chatResp.Usage = &providers.Usage{
|
|
PromptTokens: oaiResp.Usage.PromptTokens,
|
|
CompletionTokens: oaiResp.Usage.CompletionTokens,
|
|
TotalTokens: oaiResp.Usage.TotalTokens,
|
|
}
|
|
}
|
|
return chatResp, nil
|
|
}
|