fix: drop web search guidance on the completions api fallback

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-08-07 17:56:34 +02:00
co-authored by Claude Opus 5
parent a3872d3cf3
commit d037244aed
3 changed files with 48 additions and 6 deletions
+3 -5
View File
@@ -2011,11 +2011,9 @@
- authentication uses Holded's own key header rather than a bearer token
- the result stays an AI draft and is not deployed
# The fixture hub carries two Baremetrics scripts (List Sources, Create Customer) and
# none for annotations, so no hub script does what is asked. The integration's
# conventions — a `Baremetrics` resource type whose single field is `apiKey`, bearer
# auth, the api.baremetrics.com/v1 base — are only knowable by reading one of those
# other scripts, which is what makes this a test of using a near-miss as an example.
# The fixture hub carries two Baremetrics scripts, neither for annotations, so nothing
# does what is asked. Its conventions — an `apiKey` resource, bearer auth, the
# api.baremetrics.com/v1 base — are only knowable by reading one of those near-misses.
- id: global-hub3-integration-as-example
prompt: |-
Write me a script that adds an annotation to my Baremetrics account.
@@ -376,6 +376,44 @@ describe('runChatLoop onBeforeIteration web search sync', () => {
})
})
// The Completions API has no web-search tool at all, so the fallback is a third
// path (beside the two retries) that must not ship the guidance.
it('drops the web search guidance on the Completions API fallback', async () => {
let systemMessage: ChatLoopConfig['systemMessage'] = {
role: 'system',
content: 'search the web'
}
mocks.getOpenAIResponsesCompletion.mockRejectedValue(
new Error('Responses API is not enabled for this organization')
)
mocks.getCompletion.mockResolvedValue({})
const onWebSearchUnavailable = vi.fn()
await runChatLoop(
createConfig({
workspace: `workspace-${randomUUID()}`,
getSystemMessage: () => systemMessage,
onWebSearchUnavailable,
onBeforeIteration: async (_t, _h, _m, webSearch: boolean) => {
systemMessage = {
role: 'system',
content: webSearch ? 'search the web' : 'no web search'
}
}
})
)
// Falling back is per request; the model itself can still serve web search.
expect(onWebSearchUnavailable).not.toHaveBeenCalled()
expect(mocks.getCompletion).toHaveBeenCalledTimes(1)
expect(mocks.getCompletion.mock.calls[0][0][0]).toEqual({
role: 'system',
content: 'no web search'
})
})
it('sends the system message the callback rewrote on this iteration, not the next', async () => {
let systemMessage: ChatLoopConfig['systemMessage'] = { role: 'system', content: 'stale' }
const config = createConfig({
@@ -494,12 +494,18 @@ export async function runChatLoop(config: ChatLoopConfig): Promise<ChatLoopResul
}
if (useCompletionsApi) {
let completionsParams = messageParams
if (webSearch) {
console.warn(
'Web search is only supported via the OpenAI Responses API; ignoring it for the Completions API fallback.'
)
// Re-announce the effective value for the request actually being sent —
// not onWebSearchUnavailable, which means the model cannot serve web
// search at all and is cached as such; this fallback is per request.
await onBeforeIteration?.(tools, helpers, modelProvider, false)
completionsParams = messageParamsFor(config.systemMessage)
}
const completion = await getCompletion(messageParams, abortController, toolDefs, {
const completion = await getCompletion(completionsParams, abortController, toolDefs, {
forceCompletions: true,
forceModelProvider: modelProvider,
openaiClient: clients.openai,