fix(executor): strip Vertex OpenAI response tool call IDs for consistency

- Integrated `StripVertexOpenAIResponsesToolCallIDs` to remove tool call ID data from request bodies and translated requests.
- Ensures uniformity and avoids unnecessary payload data propagation.

Fixed: #2549
This commit is contained in:
Luis Pater
2026-05-04 17:54:16 +08:00
parent 85c0150653
commit bf6fa402e2
2 changed files with 49 additions and 0 deletions

View File

@@ -338,6 +338,7 @@ func (e *GeminiVertexExecutor) executeWithServiceAccount(ctx context.Context, au
requestPath := helps.PayloadRequestPath(opts)
body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath)
body, _ = sjson.SetBytes(body, "model", baseModel)
body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String())
}
action := getVertexAction(baseModel, false)
@@ -459,6 +460,7 @@ func (e *GeminiVertexExecutor) executeWithAPIKey(ctx context.Context, auth *clip
requestPath := helps.PayloadRequestPath(opts)
body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath)
body, _ = sjson.SetBytes(body, "model", baseModel)
body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String())
action := getVertexAction(baseModel, false)
if req.Metadata != nil {
@@ -570,6 +572,7 @@ func (e *GeminiVertexExecutor) executeStreamWithServiceAccount(ctx context.Conte
requestPath := helps.PayloadRequestPath(opts)
body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath)
body, _ = sjson.SetBytes(body, "model", baseModel)
body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String())
action := getVertexAction(baseModel, true)
baseURL := vertexBaseURL(location)
@@ -700,6 +703,7 @@ func (e *GeminiVertexExecutor) executeStreamWithAPIKey(ctx context.Context, auth
requestPath := helps.PayloadRequestPath(opts)
body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath)
body, _ = sjson.SetBytes(body, "model", baseModel)
body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String())
action := getVertexAction(baseModel, true)
// For API key auth, use simpler URL format without project/location
@@ -818,6 +822,7 @@ func (e *GeminiVertexExecutor) countTokensWithServiceAccount(ctx context.Context
translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq)
translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel)
translatedReq = helps.StripVertexOpenAIResponsesToolCallIDs(translatedReq, from.String())
respCtx := context.WithValue(ctx, "alt", opts.Alt)
translatedReq, _ = sjson.DeleteBytes(translatedReq, "tools")
translatedReq, _ = sjson.DeleteBytes(translatedReq, "generationConfig")
@@ -907,6 +912,7 @@ func (e *GeminiVertexExecutor) countTokensWithAPIKey(ctx context.Context, auth *
translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq)
translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel)
translatedReq = helps.StripVertexOpenAIResponsesToolCallIDs(translatedReq, from.String())
respCtx := context.WithValue(ctx, "alt", opts.Alt)
translatedReq, _ = sjson.DeleteBytes(translatedReq, "tools")
translatedReq, _ = sjson.DeleteBytes(translatedReq, "generationConfig")

View File

@@ -0,0 +1,43 @@
package helps
import (
"fmt"
"strings"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
// StripVertexOpenAIResponsesToolCallIDs removes OpenAI Responses call IDs that
// Vertex rejects in Gemini functionCall/functionResponse payloads.
func StripVertexOpenAIResponsesToolCallIDs(payload []byte, sourceFormat string) []byte {
if !strings.EqualFold(strings.TrimSpace(sourceFormat), "openai-response") {
return payload
}
contents := gjson.GetBytes(payload, "contents")
if !contents.IsArray() {
return payload
}
out := payload
for contentIndex, content := range contents.Array() {
parts := content.Get("parts")
if !parts.IsArray() {
continue
}
for partIndex, part := range parts.Array() {
if part.Get("functionCall.id").Exists() {
if updated, errDelete := sjson.DeleteBytes(out, fmt.Sprintf("contents.%d.parts.%d.functionCall.id", contentIndex, partIndex)); errDelete == nil {
out = updated
}
}
if part.Get("functionResponse.id").Exists() {
if updated, errDelete := sjson.DeleteBytes(out, fmt.Sprintf("contents.%d.parts.%d.functionResponse.id", contentIndex, partIndex)); errDelete == nil {
out = updated
}
}
}
}
return out
}