fix(gemini,anthropic): base64 validation, escaped model paths, prompt-block mapping, unified unsupported-param semantics

This commit is contained in:
2026-08-20 09:49:33 +01:00
parent 2891766b5f
commit f71d9d0b50
24 changed files with 416 additions and 45 deletions
+17 -7
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
@@ -91,15 +92,21 @@ func (a *geminiAdapter) chatCompletion(ctx context.Context, w http.ResponseWrite
}
hdr := a.requestHeaders(true)
url := a.baseURL + geminiAPIPrefix + model + ":generateContent"
// escapedModel guards against a model id smuggling extra path segments
// or query syntax ("/", "?", "..") into the upstream URL — url.PathEscape
// leaves ordinary ids (letters, digits, ":", ".", "-") untouched, per
// RFC 3986's pchar grammar, so a normal model id is byte-identical after
// escaping.
escapedModel := url.PathEscape(model)
endpoint := a.baseURL + geminiAPIPrefix + escapedModel + ":generateContent"
if streaming {
// Ask the upstream for an SSE response. The Content-Type check
// below still decides which forwarding path runs.
hdr.Set("Accept", "text/event-stream")
url = a.baseURL + geminiAPIPrefix + model + ":streamGenerateContent?alt=sse"
endpoint = a.baseURL + geminiAPIPrefix + escapedModel + ":streamGenerateContent?alt=sse"
}
resp, err := upstreamJSON(ctx, a.client, http.MethodPost, url, hdr, body)
resp, err := upstreamJSON(ctx, a.client, http.MethodPost, endpoint, hdr, body)
if err != nil {
return usage{}, err
}
@@ -189,16 +196,19 @@ func (a *geminiAdapter) embeddings(ctx context.Context, w http.ResponseWriter, r
}
var body map[string]any
var url string
var endpoint string
escapedModel := url.PathEscape(model)
if single {
body = geminiEmbedContentRequest(texts[0])
url = a.baseURL + geminiAPIPrefix + model + ":embedContent"
endpoint = a.baseURL + geminiAPIPrefix + escapedModel + ":embedContent"
} else {
// geminiBatchEmbedContentsRequest still takes the unescaped bare
// model — it writes "models/{model}" into the JSON body, not a URL.
body = geminiBatchEmbedContentsRequest(model, texts)
url = a.baseURL + geminiAPIPrefix + model + ":batchEmbedContents"
endpoint = a.baseURL + geminiAPIPrefix + escapedModel + ":batchEmbedContents"
}
resp, err := upstreamJSON(ctx, a.client, http.MethodPost, url, a.requestHeaders(true), body)
resp, err := upstreamJSON(ctx, a.client, http.MethodPost, endpoint, a.requestHeaders(true), body)
if err != nil {
return usage{}, err
}