fix(grok): enable free prompt cache for Responses function tools
Reuse applyGrokFreeMessagesFunctionToolCacheRoute on native /v1/responses and the Grok WS HTTP bridge so Free OAuth requests with client function tools get the same mixed-tools cache route as the Messages bridge (append/convert web_search and x_search). Also dedupe: Grok Build already declares function tools named web_search, so naive append caused "Duplicate tool names: web_search". Convert those function entries to native tool types and skip duplicates. Only Free OAuth accounts (isKnownGrokFreeAccount); paid/unknown unchanged.
This commit is contained in:
@@ -61,6 +61,12 @@ func (s *OpenAIGatewayService) forwardGrokResponses(
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("apply grok prompt cache identity: %w", err)
|
||||
}
|
||||
// Free OAuth + client function tools: reuse Messages mixed-tools cache route
|
||||
// (append web_search/x_search so xAI does not force non-cacheable build-free).
|
||||
patchedBody, err = applyGrokFreeMessagesFunctionToolCacheRoute(patchedBody, body, account, cacheIdentity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("apply grok Free function-tool cache route: %w", err)
|
||||
}
|
||||
|
||||
token, _, err := s.getRequestCredential(ctx, c, account)
|
||||
if err != nil {
|
||||
|
||||
@@ -102,8 +102,8 @@ func isGrokRequestContext(c *gin.Context) bool {
|
||||
// Free OAuth requests without native search tools are routed by xAI to the
|
||||
// non-cacheable build-free model. For otherwise tool-free requests, add the
|
||||
// native tools with tool_choice=none: this selects the cache-capable tier
|
||||
// without allowing an actual search. Explicit client tools are handled by the
|
||||
// narrower Messages-only mixed-tools policy below.
|
||||
// without allowing an actual search. Explicit client function tools are handled by
|
||||
// applyGrokFreeMessagesFunctionToolCacheRoute (Messages bridge and native Responses).
|
||||
func applyGrokResponsesCacheIdentity(body, intentSourceBody []byte, identity string, injectFreeTierTools bool) ([]byte, error) {
|
||||
identity = strings.TrimSpace(identity)
|
||||
if identity == "" {
|
||||
@@ -263,17 +263,35 @@ func appendMissingGrokFreeCacheNativeTools(body []byte) ([]byte, error) {
|
||||
toolType := strings.TrimSpace(tool.Get("type").String())
|
||||
switch toolType {
|
||||
case "function":
|
||||
if !tool.IsObject() || strings.TrimSpace(tool.Get("name").String()) == "" || tool.Get("function").Exists() {
|
||||
name := strings.TrimSpace(tool.Get("name").String())
|
||||
if !tool.IsObject() || name == "" || tool.Get("function").Exists() {
|
||||
return body, nil
|
||||
}
|
||||
// Grok Build may declare search as function tools. Convert to native
|
||||
// entries so Free OAuth stays cache-capable without duplicate names.
|
||||
if name == "web_search" || name == "x_search" {
|
||||
if present[name] {
|
||||
continue
|
||||
}
|
||||
raw, err := json.Marshal(map[string]string{"type": name})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
merged = append(merged, raw)
|
||||
present[name] = true
|
||||
continue
|
||||
}
|
||||
hasFunction = true
|
||||
merged = append(merged, json.RawMessage(tool.Raw))
|
||||
case "web_search", "x_search":
|
||||
// Native tools may already be present when this helper is retried.
|
||||
if present[toolType] {
|
||||
continue
|
||||
}
|
||||
merged = append(merged, json.RawMessage(tool.Raw))
|
||||
present[toolType] = true
|
||||
default:
|
||||
return body, nil
|
||||
}
|
||||
merged = append(merged, json.RawMessage(tool.Raw))
|
||||
present[toolType] = true
|
||||
}
|
||||
if !hasFunction {
|
||||
return body, nil
|
||||
|
||||
@@ -201,6 +201,11 @@ func (s *OpenAIGatewayService) proxyOpenAIWSHTTPBridgeTurn(
|
||||
releaseUpstreamCtx()
|
||||
return nil, fmt.Errorf("apply grok prompt cache identity: %w", err)
|
||||
}
|
||||
body, err = applyGrokFreeMessagesFunctionToolCacheRoute(body, grokIntentSourceBody, account, grokCacheIdentity)
|
||||
if err != nil {
|
||||
releaseUpstreamCtx()
|
||||
return nil, fmt.Errorf("apply grok Free function-tool cache route: %w", err)
|
||||
}
|
||||
upstreamReq, err = buildGrokResponsesRequest(upstreamCtx, c, account, body, token, grokCacheIdentity, s.cfg)
|
||||
} else {
|
||||
upstreamReq, err = s.buildUpstreamRequestOpenAIPassthrough(upstreamCtx, c, account, body, token)
|
||||
|
||||
Reference in New Issue
Block a user