more oc fixes

This commit is contained in:
Luxferre
2026-09-18 12:08:01 +03:00
parent 0b2d9f2a90
commit f23c9132ce
3 changed files with 162 additions and 26 deletions
+58 -18
View File
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
@@ -87,6 +88,8 @@ var llmTransport = &http.Transport{
var defCfg = Cfg{"https://api.kilo.ai/api/openrouter", "openrouter/free", "-", 0.7, 300, 120, 1000, true, "auto", 262144, 15000, map[string]string{"reasoning_effort": "high"}}
const opencodeAgentVersion = "opencode/1.18.31"
const opencodeProviderUA = "ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14"
const opencodeIDAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
func atoiD(s string, d int) int {
if v, e := strconv.Atoi(strings.TrimSpace(s)); e == nil {
@@ -105,12 +108,7 @@ func queryModelsContextWindow(cfg *Cfg) int {
}
req, err := http.NewRequest("GET", strings.TrimRight(cfg.Endpoint, "/")+"/models", nil)
if err != nil { return 0 }
req.Header.Set("User-Agent", opencodeAgentVersion)
req.Header.Set("X-Session-Id", projectID())
req.Header.Set("x-opencode-project", projectID())
req.Header.Set("x-opencode-client", "cli")
req.Header.Set("x-opencode-session", opencodeSessionID())
if cfg.APIKey != "" && cfg.APIKey != "-" { req.Header.Set("Authorization", "Bearer "+cfg.APIKey) }
applyLLMHeaders(req, cfg, ocRequestID())
resp, err := client.Do(req)
if err != nil || resp.StatusCode >= 400 { return 0 }
defer resp.Body.Close()
@@ -181,12 +179,7 @@ func listModels(cfg *Cfg) (string, error) {
}
req, err := http.NewRequest("GET", strings.TrimRight(cfg.Endpoint, "/")+"/models", nil)
if err != nil { return "", err }
req.Header.Set("User-Agent", opencodeAgentVersion)
req.Header.Set("X-Session-Id", projectID())
req.Header.Set("x-opencode-project", projectID())
req.Header.Set("x-opencode-client", "cli")
req.Header.Set("x-opencode-session", opencodeSessionID())
if cfg.APIKey != "" && cfg.APIKey != "-" { req.Header.Set("Authorization", "Bearer "+cfg.APIKey) }
applyLLMHeaders(req, cfg, ocRequestID())
resp, err := client.Do(req)
if err != nil { return "", err }
defer resp.Body.Close()
@@ -1052,6 +1045,7 @@ func llm(ctx context.Context, cfg *Cfg, msgs []Message, tools []map[string]any)
pend := c("...requesting...", 1, 2)
var resp *http.Response
var err error
reqID := ocRequestID()
for i := 0; i <= len(fib); i++ {
if err := ctx.Err(); err != nil {
if COL { fmt.Print("\r\033[K") }
@@ -1060,12 +1054,7 @@ func llm(ctx context.Context, cfg *Cfg, msgs []Message, tools []map[string]any)
if COL { fmt.Print("\r" + pend) } else { fmt.Println(pend) }
req, _ := http.NewRequestWithContext(ctx, "POST", strings.TrimRight(cfg.Endpoint, "/")+"/chat/completions", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", opencodeAgentVersion)
req.Header.Set("X-Session-Id", projectID())
req.Header.Set("x-opencode-project", projectID())
req.Header.Set("x-opencode-client", "cli")
req.Header.Set("x-opencode-session", opencodeSessionID())
if cfg.APIKey != "" && cfg.APIKey != "-" { req.Header.Set("Authorization", "Bearer "+cfg.APIKey) }
applyLLMHeaders(req, cfg, reqID)
resp, err = client.Do(req)
var is4xxClientErr bool
if err == nil && resp.StatusCode >= 400 {
@@ -1694,6 +1683,57 @@ func opencodeSessionID() string {
return "ses_" + pid
}
// isOpencodeEndpoint reports whether the endpoint belongs to OpenCode Zen and
// therefore expects the x-opencode-* request headers.
func isOpencodeEndpoint(endpoint string) bool {
u, err := url.Parse(strings.TrimSpace(endpoint))
if err != nil || u.Hostname() == "" {
return strings.Contains(strings.ToLower(endpoint), "opencode.ai")
}
h := strings.ToLower(u.Hostname())
return h == "opencode.ai" || strings.HasSuffix(h, ".opencode.ai")
}
// ocRequestID returns a fresh OpenCode-style message id (msg_ followed by 26
// base32 characters) matching the ids sent in the x-opencode-request header.
func ocRequestID() string {
buf := make([]byte, 26)
if _, err := rand.Read(buf); err != nil {
h := md5.Sum([]byte(fmt.Sprintf("%d-%d", time.Now().UnixNano(), os.Getpid())))
for i := range buf {
buf[i] = h[i%len(h)]
}
}
var b strings.Builder
b.WriteString("msg_")
for _, x := range buf {
b.WriteByte(opencodeIDAlphabet[int(x)&31])
}
return b.String()
}
// applyLLMHeaders sets the request headers appropriate for the configured
// endpoint. OpenCode (Zen) endpoints receive the x-opencode-* family plus the
// full provider User-Agent; all other endpoints keep the legacy
// session-affinity headers. msgID is used for the x-opencode-request header.
func applyLLMHeaders(req *http.Request, cfg *Cfg, msgID string) {
sid := opencodeSessionID()
if isOpencodeEndpoint(cfg.Endpoint) {
req.Header.Set("User-Agent", opencodeAgentVersion+" "+opencodeProviderUA)
req.Header.Set("x-opencode-client", "cli")
req.Header.Set("x-opencode-project", projectID())
req.Header.Set("x-opencode-session", sid)
req.Header.Set("x-opencode-request", msgID)
} else {
req.Header.Set("User-Agent", opencodeAgentVersion)
req.Header.Set("x-session-affinity", sid)
req.Header.Set("X-Session-Id", sid)
}
if cfg.APIKey != "" && cfg.APIKey != "-" {
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
}
}
func saveSession(msgs []Message, sid string) (string, string) {
sid = strings.TrimSpace(sid)
if sid == "" {