mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
fix(grok): default client tool caching on for Free accounts
This commit is contained in:
@@ -155,12 +155,12 @@ func hasGrokResponsesToolIntent(body []byte) bool {
|
||||
}
|
||||
|
||||
// applyGrokFreeMessagesFunctionToolCacheRoute enables xAI's cache-capable
|
||||
// mixed-tools route only for known Free accounts. Requests that already expose
|
||||
// search capability can use the route automatically. Pure client-tool requests
|
||||
// require an account-level opt-in because adding native search changes xAI's
|
||||
// automatic tool selection behavior (#4486).
|
||||
// mixed-tools route only for known Free accounts. Pure client tools default to
|
||||
// the cache-capable route so an intermediate sub2api does not need to preserve
|
||||
// client-specific opt-in headers. Operators can explicitly disable this per
|
||||
// account when native search tools would change the desired behavior (#4486).
|
||||
func applyGrokFreeMessagesFunctionToolCacheRoute(body, intentSourceBody []byte, account *Account, cacheIdentity string) ([]byte, error) {
|
||||
allowPureClientTools := account != nil && account.getExtraBool(grokClientToolCacheOptInExtraKey)
|
||||
allowPureClientTools, _ := grokClientToolCacheAccountPolicy(account)
|
||||
return applyGrokFreeToolCacheRoute(body, intentSourceBody, account, cacheIdentity, allowPureClientTools, true)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ func applyGrokFreeMessagesFunctionToolCacheRoute(body, intentSourceBody []byte,
|
||||
// sub2api header is consumed locally because buildGrokResponsesRequest only
|
||||
// forwards the explicitly supported OpenAI-Beta header from downstream.
|
||||
func applyGrokFreeRequestToolCacheRoute(c *gin.Context, body, intentSourceBody []byte, account *Account, cacheIdentity string) ([]byte, error) {
|
||||
allowPureClientTools := account != nil && account.getExtraBool(grokClientToolCacheOptInExtraKey)
|
||||
allowPureClientTools, accountPolicyExplicit := grokClientToolCacheAccountPolicy(account)
|
||||
requestOptOut := false
|
||||
if c != nil {
|
||||
switch strings.ToLower(strings.TrimSpace(c.GetHeader(grokClientToolCacheOptInHeader))) {
|
||||
@@ -179,16 +179,39 @@ func applyGrokFreeRequestToolCacheRoute(c *gin.Context, body, intentSourceBody [
|
||||
requestOptOut = true
|
||||
}
|
||||
}
|
||||
if !allowPureClientTools && !requestOptOut && isGrokClaudeDesktopResponsesCacheRequest(c) {
|
||||
if !allowPureClientTools && !accountPolicyExplicit && !requestOptOut && isGrokClaudeDesktopResponsesCacheRequest(c) {
|
||||
allowPureClientTools = true
|
||||
}
|
||||
// A function merely named web_search/x_search is still a client function.
|
||||
// Native Responses search types are safe to recognize automatically, while
|
||||
// converting a client function requires either explicit opt-in or the strict
|
||||
// Claude Desktop Responses fingerprint used above (#4486).
|
||||
// Known Free OAuth accounts use the cache route by default; a request-scoped
|
||||
// opt-in may override an account opt-out, while an explicit request opt-out
|
||||
// always wins. The legacy Claude fingerprint remains only as a compatibility
|
||||
// fallback when no account policy has been recorded (#4486).
|
||||
return applyGrokFreeToolCacheRoute(body, intentSourceBody, account, cacheIdentity, allowPureClientTools, allowPureClientTools)
|
||||
}
|
||||
|
||||
// grokClientToolCacheAccountPolicy is intentionally strict for configured
|
||||
// values: only a JSON boolean is accepted. A missing key defaults on solely for
|
||||
// accounts positively identified as Grok Free OAuth; paid, API-key, and unknown
|
||||
// accounts remain fail-closed.
|
||||
func grokClientToolCacheAccountPolicy(account *Account) (enabled, explicit bool) {
|
||||
if !isKnownGrokFreeAccount(account) {
|
||||
return false, false
|
||||
}
|
||||
if account.Extra == nil {
|
||||
return true, false
|
||||
}
|
||||
value, exists := account.Extra[grokClientToolCacheOptInExtraKey]
|
||||
if !exists {
|
||||
return true, false
|
||||
}
|
||||
enabled, valid := value.(bool)
|
||||
if !valid {
|
||||
return false, true
|
||||
}
|
||||
return enabled, true
|
||||
}
|
||||
|
||||
// isGrokClaudeDesktopResponsesCacheRequest recognizes the strict wire
|
||||
// fingerprint emitted when Claude Desktop's local agent is translated by
|
||||
// CC Switch into an OpenAI Responses request. Requiring every independent
|
||||
|
||||
@@ -197,7 +197,7 @@ func TestApplyGrokCacheIdentityWritesResponsesBodyAndHeader(t *testing.T) {
|
||||
require.False(t, gjson.GetBytes(unscopedBody, "tool_choice").Exists())
|
||||
}
|
||||
|
||||
func TestApplyGrokCacheIdentityAppendsNativeToolsToResponseFunctions(t *testing.T) {
|
||||
func TestGrokFreeMessagesClientToolCacheDefaultsOnForKnownFree(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(901, "access-token")
|
||||
account.Credentials["subscription_tier"] = " FREE "
|
||||
tests := []struct {
|
||||
@@ -211,7 +211,6 @@ func TestApplyGrokCacheIdentityAppendsNativeToolsToResponseFunctions(t *testing.
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Pure client function tools without search → no native injection (#4486).
|
||||
intentBody := []byte(`{"model":"grok","tools":[{"type":"function","name":"lookup","description":"look up a value","parameters":{"type":"object"}},{"type":"function","name":"save","parameters":{"type":"object"}}]` + tt.toolChoiceJSON + `}`)
|
||||
body, err := applyGrokResponsesCacheIdentity(intentBody, intentBody, "isolated-id", true)
|
||||
require.NoError(t, err)
|
||||
@@ -220,16 +219,46 @@ func TestApplyGrokCacheIdentityAppendsNativeToolsToResponseFunctions(t *testing.
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "isolated-id", gjson.GetBytes(body, "prompt_cache_key").String())
|
||||
tools := gjson.GetBytes(body, "tools").Array()
|
||||
require.Len(t, tools, 2, "pure client functions should not get native search injected")
|
||||
require.Len(t, tools, 4)
|
||||
require.Equal(t, "function", tools[0].Get("type").String())
|
||||
require.Equal(t, "lookup", tools[0].Get("name").String())
|
||||
require.Equal(t, "function", tools[1].Get("type").String())
|
||||
require.Equal(t, "save", tools[1].Get("name").String())
|
||||
require.Equal(t, "web_search", tools[2].Get("type").String())
|
||||
require.Equal(t, "x_search", tools[3].Get("type").String())
|
||||
require.Equal(t, tt.wantChoice, gjson.GetBytes(body, "tool_choice").Exists())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrokFreeMessagesClientToolCacheDefaultsWithMissingAccountSetting(t *testing.T) {
|
||||
body := []byte(`{"model":"grok","tools":[{"type":"function","name":"view_image","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
tests := []struct {
|
||||
name string
|
||||
extra map[string]any
|
||||
}{
|
||||
{name: "nil extra"},
|
||||
{name: "empty extra", extra: map[string]any{}},
|
||||
{name: "unrelated extra", extra: map[string]any{"other_setting": true}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90101, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
account.Extra = tt.extra
|
||||
|
||||
patched, err := applyGrokFreeMessagesFunctionToolCacheRoute(body, body, account, "isolated-id")
|
||||
|
||||
require.NoError(t, err)
|
||||
tools := gjson.GetBytes(patched, "tools").Array()
|
||||
require.Len(t, tools, 3)
|
||||
require.Equal(t, "web_search", tools[1].Get("type").String())
|
||||
require.Equal(t, "x_search", tools[2].Get("type").String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyGrokCacheIdentityAppendsNativeToolsWhenSearchPresent(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(901, "access-token")
|
||||
account.Credentials["subscription_tier"] = " FREE "
|
||||
@@ -268,9 +297,36 @@ func TestGrokFreeClientToolCacheAccountOptIn(t *testing.T) {
|
||||
require.Equal(t, "x_search", tools[3].Get("type").String())
|
||||
}
|
||||
|
||||
func TestGrokFreeClientToolCacheRequestOptIn(t *testing.T) {
|
||||
func TestGrokFreeMessagesClientToolCacheAccountOptOut(t *testing.T) {
|
||||
body := []byte(`{"model":"grok","tools":[{"type":"function","name":"view_image","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
tests := []struct {
|
||||
name string
|
||||
value any
|
||||
}{
|
||||
{name: "explicit false", value: false},
|
||||
{name: "string false is malformed", value: "false"},
|
||||
{name: "numeric value is malformed", value: 1},
|
||||
{name: "null value is malformed", value: nil},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90111, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
account.Extra = map[string]any{grokClientToolCacheOptInExtraKey: tt.value}
|
||||
|
||||
patched, err := applyGrokFreeMessagesFunctionToolCacheRoute(body, body, account, "isolated-id")
|
||||
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(body), string(patched))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrokFreeClientToolCacheRequestOptInOverridesAccountOptOut(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(9014, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
account.Extra = map[string]any{grokClientToolCacheOptInExtraKey: false}
|
||||
c := newGrokCacheTestContext(9014)
|
||||
c.Request.Header.Set(grokClientToolCacheOptInHeader, "prefer-cache")
|
||||
intentBody := []byte(`{"model":"grok","tools":[{"type":"function","name":"view_image","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
@@ -287,6 +343,23 @@ func TestGrokFreeClientToolCacheRequestOptIn(t *testing.T) {
|
||||
require.Equal(t, "x_search", tools[2].Get("type").String())
|
||||
}
|
||||
|
||||
func TestGrokFreeChatRequestClientToolCacheDefaultsOnWithoutClientFingerprint(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90140, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
c := newGrokCacheTestContext(90140)
|
||||
c.Request.URL.Path = "/v1/chat/completions"
|
||||
body := []byte(`{"model":"grok","tools":[{"type":"function","name":"view_image","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
|
||||
patched, err := applyGrokFreeRequestToolCacheRoute(c, body, body, account, "isolated-id")
|
||||
|
||||
require.NoError(t, err)
|
||||
tools := gjson.GetBytes(patched, "tools").Array()
|
||||
require.Len(t, tools, 3)
|
||||
require.Equal(t, "view_image", tools[0].Get("name").String())
|
||||
require.Equal(t, "web_search", tools[1].Get("type").String())
|
||||
require.Equal(t, "x_search", tools[2].Get("type").String())
|
||||
}
|
||||
|
||||
func TestGrokFreeClientToolCacheClaudeDesktopResponsesAutoOptIn(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90141, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
@@ -318,6 +391,7 @@ func TestGrokFreeClientToolCacheClaudeDesktopResponsesAutoOptIn(t *testing.T) {
|
||||
func TestGrokFreeClientToolCacheClaudeDesktopFingerprintRequiresAllSignals(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90142, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
account.Extra = map[string]any{grokClientToolCacheOptInExtraKey: false}
|
||||
body := []byte(`{"model":"grok","tools":[{"type":"function","name":"Read","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
|
||||
tests := []struct {
|
||||
@@ -416,19 +490,15 @@ func TestGrokFreeClientToolCacheClaudeDesktopFingerprintRequiresAllSignals(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrokFreeClientToolCacheClaudeDesktopExplicitRequestOptOut(t *testing.T) {
|
||||
func TestGrokFreeClientToolCacheExplicitRequestOptOut(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(90144, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
account.Extra = map[string]any{grokClientToolCacheOptInExtraKey: true}
|
||||
body := []byte(`{"model":"grok","tools":[{"type":"function","name":"Read","parameters":{"type":"object"}}],"tool_choice":"auto"}`)
|
||||
|
||||
for _, value := range []string{"0", "false", "no", "off"} {
|
||||
t.Run(value, func(t *testing.T) {
|
||||
c := newGrokCacheTestContext(90144)
|
||||
c.Request.Header.Set("User-Agent", "claude-cli/2.1.215 (external, claude-desktop-3p, agent-sdk/0.3.215)")
|
||||
c.Request.Header.Set("X-App", "cli")
|
||||
c.Request.Header.Set("anthropic-client-platform", "desktop_app")
|
||||
c.Request.Header.Set("X-Claude-Code-Session-Id", "desktop-session-1")
|
||||
c.Request.URL.Path = "/v1/chat/completions"
|
||||
c.Request.Header.Set(grokClientToolCacheOptInHeader, value)
|
||||
|
||||
patched, err := applyGrokFreeRequestToolCacheRoute(c, body, body, account, "isolated-id")
|
||||
@@ -455,7 +525,7 @@ func TestGrokFreeClientToolCacheClaudeDesktopAutoOptInDoesNotOverridePaidTier(t
|
||||
require.JSONEq(t, string(body), string(patched))
|
||||
}
|
||||
|
||||
func TestGrokFreeRequestClientSearchFunctionRequiresOptIn(t *testing.T) {
|
||||
func TestGrokFreeRequestClientSearchFunctionUsesDefaultAccountPolicy(t *testing.T) {
|
||||
account := healthyGrokOAuthGatewayTestAccount(9015, "access-token")
|
||||
account.Credentials["subscription_tier"] = "free"
|
||||
c := newGrokCacheTestContext(9015)
|
||||
@@ -464,7 +534,11 @@ func TestGrokFreeRequestClientSearchFunctionRequiresOptIn(t *testing.T) {
|
||||
patched, err := applyGrokFreeRequestToolCacheRoute(c, body, body, account, "isolated-id")
|
||||
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(body), string(patched))
|
||||
tools := gjson.GetBytes(patched, "tools").Array()
|
||||
require.Len(t, tools, 3)
|
||||
require.Equal(t, "view_image", tools[0].Get("name").String())
|
||||
require.Equal(t, "web_search", tools[1].Get("type").String())
|
||||
require.Equal(t, "x_search", tools[2].Get("type").String())
|
||||
}
|
||||
|
||||
func TestGrokFreeRequestToolChoiceNoneUsesSafeCacheRoute(t *testing.T) {
|
||||
@@ -539,7 +613,7 @@ func TestApplyGrokCacheIdentityRequiresPatchedFunctionTools(t *testing.T) {
|
||||
{name: "missing tools", patchedBody: `{"model":"grok-4.5"}`},
|
||||
{name: "empty tools", patchedBody: `{"model":"grok-4.5","tools":[]}`},
|
||||
{name: "native tools only", patchedBody: `{"model":"grok-4.5","tools":[{"type":"web_search"}]}`},
|
||||
{name: "unexpected patched tool", patchedBody: `{"model":"grok-4.5","tools":[{"type":"function","name":"lookup"},{"type":"mcp","name":"server"}]}`},
|
||||
{name: "unexpected patched tool", patchedBody: `{"model":"grok-4.5","tools":[{"type":"function","name":"lookup"},{"type":"namespace","name":"server"}]}`},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -559,8 +633,7 @@ func TestApplyGrokCacheIdentityRequiresPatchedFunctionTools(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGrokFreeMessagesFunctionToolCacheRouteRequiresKnownFreeTier(t *testing.T) {
|
||||
// Include web_search as function to trigger native tool injection (pure client
|
||||
// functions no longer trigger injection after #4486 fix).
|
||||
// Include web_search as a function to also cover its conversion to a native tool.
|
||||
intentBody := []byte(`{"model":"grok","tools":[{"type":"function","name":"lookup"},{"type":"function","name":"web_search"}],"tool_choice":"auto"}`)
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -2768,7 +2768,9 @@ const headerOverrideCapable = computed(
|
||||
// Grok OAuth 自定义上游地址(仅转发端点;OAuth 授权/令牌刷新不受影响)
|
||||
const grokOAuthCustomBaseUrlEnabled = ref(false)
|
||||
const grokOAuthBaseUrl = ref('')
|
||||
const grokClientToolCacheEnabled = ref(false)
|
||||
// Grok Free OAuth accounts use client-tool prompt caching by default. Keep an
|
||||
// explicit false in the account extra as the opt-out signal.
|
||||
const grokClientToolCacheEnabled = ref(true)
|
||||
|
||||
const interceptWarmupRequests = ref(false)
|
||||
const autoPauseOnExpired = ref(false)
|
||||
@@ -3424,10 +3426,14 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
// Load Grok OAuth custom upstream URL state(存储的官方地址视同未定制)
|
||||
grokOAuthCustomBaseUrlEnabled.value = false
|
||||
grokOAuthBaseUrl.value = ''
|
||||
const grokClientToolCacheSetting =
|
||||
newAccount.platform === 'grok' && newAccount.type === 'oauth'
|
||||
? newAccount.extra?.[GROK_CLIENT_TOOL_CACHE_EXTRA_KEY]
|
||||
: undefined
|
||||
grokClientToolCacheEnabled.value =
|
||||
newAccount.platform === 'grok' &&
|
||||
newAccount.type === 'oauth' &&
|
||||
newAccount.extra?.[GROK_CLIENT_TOOL_CACHE_EXTRA_KEY] === true
|
||||
(grokClientToolCacheSetting === undefined || grokClientToolCacheSetting === true)
|
||||
if (newAccount.platform === 'grok' && newAccount.type === 'oauth' && newAccount.credentials) {
|
||||
const grokCreds = newAccount.credentials as Record<string, unknown>
|
||||
if (isCustomGrokBaseUrl(grokCreds.base_url)) {
|
||||
@@ -4318,11 +4324,9 @@ const handleSubmit = async () => {
|
||||
const newExtra: Record<string, unknown> = {
|
||||
...((props.account.extra as Record<string, unknown>) || {})
|
||||
}
|
||||
if (grokClientToolCacheEnabled.value) {
|
||||
newExtra[GROK_CLIENT_TOOL_CACHE_EXTRA_KEY] = true
|
||||
} else {
|
||||
delete newExtra[GROK_CLIENT_TOOL_CACHE_EXTRA_KEY]
|
||||
}
|
||||
// Persist both states so a disabled account remains opted out when the
|
||||
// backend applies the default-enabled policy to missing values.
|
||||
newExtra[GROK_CLIENT_TOOL_CACHE_EXTRA_KEY] = grokClientToolCacheEnabled.value
|
||||
updatePayload.extra = newExtra
|
||||
}
|
||||
|
||||
|
||||
@@ -264,18 +264,17 @@ describe('EditAccountModal Grok OAuth upstream config', () => {
|
||||
|
||||
const payload = updateAccountMock.mock.calls[0]?.[1]
|
||||
expect(payload?.extra?.custom_setting).toBe('keep-me')
|
||||
expect(payload?.extra).not.toHaveProperty('grok_client_tool_cache_enabled')
|
||||
expect(payload?.extra?.grok_client_tool_cache_enabled).toBe(false)
|
||||
})
|
||||
|
||||
it('enables client-tool caching while preserving unrelated extra fields', async () => {
|
||||
it('defaults client-tool caching on when the setting is missing and persists explicit true', async () => {
|
||||
const account = buildGrokOAuthAccount({}, { custom_setting: 'keep-me' })
|
||||
updateAccountMock.mockResolvedValue(account)
|
||||
|
||||
const wrapper = mountModal(account)
|
||||
const toggle = wrapper.get('[data-testid="grok-client-tool-cache-toggle"]')
|
||||
expect(toggle.attributes('aria-checked')).toBe('false')
|
||||
expect(toggle.attributes('aria-checked')).toBe('true')
|
||||
|
||||
await toggle.trigger('click')
|
||||
await wrapper.get('form#edit-account-form').trigger('submit.prevent')
|
||||
await vi.waitFor(() => expect(updateAccountMock).toHaveBeenCalledTimes(1))
|
||||
|
||||
@@ -285,4 +284,43 @@ describe('EditAccountModal Grok OAuth upstream config', () => {
|
||||
custom_setting: 'keep-me'
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps an explicit false opt-out when saving an untouched account', async () => {
|
||||
const account = buildGrokOAuthAccount(
|
||||
{},
|
||||
{
|
||||
grok_client_tool_cache_enabled: false,
|
||||
custom_setting: 'keep-me'
|
||||
}
|
||||
)
|
||||
updateAccountMock.mockResolvedValue(account)
|
||||
|
||||
const wrapper = mountModal(account)
|
||||
const toggle = wrapper.get('[data-testid="grok-client-tool-cache-toggle"]')
|
||||
expect(toggle.attributes('aria-checked')).toBe('false')
|
||||
|
||||
await wrapper.get('form#edit-account-form').trigger('submit.prevent')
|
||||
await vi.waitFor(() => expect(updateAccountMock).toHaveBeenCalledTimes(1))
|
||||
|
||||
const payload = updateAccountMock.mock.calls[0]?.[1]
|
||||
expect(payload?.extra).toMatchObject({
|
||||
grok_client_tool_cache_enabled: false,
|
||||
custom_setting: 'keep-me'
|
||||
})
|
||||
})
|
||||
|
||||
it('shows malformed cache settings as disabled so the UI matches the fail-closed backend', () => {
|
||||
const account = buildGrokOAuthAccount(
|
||||
{},
|
||||
{
|
||||
grok_client_tool_cache_enabled: 'true',
|
||||
custom_setting: 'keep-me'
|
||||
}
|
||||
)
|
||||
|
||||
const wrapper = mountModal(account)
|
||||
expect(
|
||||
wrapper.get('[data-testid="grok-client-tool-cache-toggle"]').attributes('aria-checked')
|
||||
).toBe('false')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -643,7 +643,7 @@ export default {
|
||||
},
|
||||
grokClientToolCache: {
|
||||
title: 'Client Tool Cache (May Change Automatic Tool Selection)',
|
||||
hint: 'For detected Grok Free OAuth accounts, enable upstream prompt caching for client function tools such as Codex and Trae. Enable only after confirming the automatic tool-selection behavior is acceptable.'
|
||||
hint: 'For detected Grok Free OAuth accounts, this is enabled by default for client function tools such as Codex and Trae. Turn it off to opt out if the automatic tool-selection behavior is not acceptable.'
|
||||
},
|
||||
autoPauseOnExpired: 'Auto Pause On Expired',
|
||||
autoPauseOnExpiredDesc: 'When enabled, the account will auto pause scheduling after it expires',
|
||||
|
||||
@@ -736,7 +736,7 @@ export default {
|
||||
},
|
||||
grokClientToolCache: {
|
||||
title: '客户端工具缓存(可能改变自动工具选择)',
|
||||
hint: '仅对已识别为 Free 的 Grok OAuth 账号生效,为 Codex、Trae 等客户端函数工具请求启用上游提示缓存;请确认自动工具选择行为可接受后开启。'
|
||||
hint: '仅对已识别为 Free 的 Grok OAuth 账号生效,默认会为 Codex、Trae 等客户端函数工具请求启用上游提示缓存;如不接受自动工具选择行为,可关闭此开关退出。'
|
||||
},
|
||||
autoPauseOnExpired: '过期自动暂停调度',
|
||||
autoPauseOnExpiredDesc: '启用后,账号过期将自动暂停调度',
|
||||
|
||||
Reference in New Issue
Block a user