From a7772bf181ee6b706c32da864e5f42595c1c2d0b Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:34:55 +0000 Subject: [PATCH 1/6] update model gpt 5.6 on the open ai model Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/utils/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 5b4f03e1..b5801893 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -67,14 +67,15 @@ export async function getModel(requireVision: boolean = false) { console.error('User selected "Gemini 3.1 Pro" but GEMINI_3_PRO_API_KEY is not set.'); throw new Error('Selected model is not configured.'); } + case 'GPT-5.6': case 'GPT-5.1': if (openaiApiKey) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-4o'); + return openai('gpt-5.6'); } else { - console.error('User selected "GPT-5.1" but OPENAI_API_KEY is not set.'); + console.error('User selected "GPT-5.6" but OPENAI_API_KEY is not set.'); throw new Error('Selected model is not configured.'); } } @@ -86,7 +87,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-4o'); + return openai('gpt-5.6'); } catch (error) { console.warn('OpenAI API unavailable, falling back to next provider:', error); } @@ -135,7 +136,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-4o'); + return openai('gpt-5.6'); } /** From ded191f860488051253d8697a046a764d8a535a4 Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:51:27 +0000 Subject: [PATCH 2/6] update model gpt 5.6 on the open ai model and expand token limits Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/agents/researcher.tsx | 2 +- lib/agents/resolution-search.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 2004485f..13c313b7 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -159,7 +159,7 @@ export async function researcher( const result = await nonexperimental_streamText({ model: (await getModel(hasImage)) as LanguageModel, - maxTokens: 2500, + maxTokens: 4096, temperature: 0, // Allow multi-step tool calling (tool round + synthesis step with headroom for chained tool calls) maxSteps: 5, diff --git a/lib/agents/resolution-search.tsx b/lib/agents/resolution-search.tsx index c99b42e0..b2908d43 100644 --- a/lib/agents/resolution-search.tsx +++ b/lib/agents/resolution-search.tsx @@ -194,7 +194,7 @@ Analyze the user's prompt and the image to provide a holistic understanding of t messages: filteredMessages, schema: resolutionSearchSchema, temperature: 0, - maxTokens: 1800, + maxTokens: 4096, abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), })), AI_REQUEST_TIMEOUT_MS, 'Resolution analysis') } From 94f97defeed49c71ddc0d1f614decd6b14a9c55e Mon Sep 17 00:00:00 2001 From: ngoiyaeric <1.15367894e+08+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:11:21 +0000 Subject: [PATCH 3/6] C-x fix: preserve streamed output with GPT-5.6 --- lib/agents/researcher.tsx | 6 ++++-- lib/agents/resolution-search.tsx | 6 ++++-- lib/utils/index.ts | 12 ++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 13c313b7..d72be6e0 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -10,7 +10,7 @@ import { import { Section } from '@/components/section' import { BotMessage } from '@/components/message' import { getTools } from './tools' -import { getModel } from '../utils' +import { getModel, getReasoningProviderOptions } from '../utils' import { MapProvider } from '@/lib/store/settings' import { DrawnFeature } from './resolution-search' import { getSelectedModel } from '@/lib/actions/users' @@ -157,10 +157,12 @@ export async function researcher( : 'none' }) + const model = await getModel(hasImage) const result = await nonexperimental_streamText({ - model: (await getModel(hasImage)) as LanguageModel, + model: model as LanguageModel, maxTokens: 4096, temperature: 0, + providerOptions: getReasoningProviderOptions(model), // Allow multi-step tool calling (tool round + synthesis step with headroom for chained tool calls) maxSteps: 5, abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), diff --git a/lib/agents/resolution-search.tsx b/lib/agents/resolution-search.tsx index b2908d43..1fc85edb 100644 --- a/lib/agents/resolution-search.tsx +++ b/lib/agents/resolution-search.tsx @@ -1,5 +1,5 @@ import { CoreMessage, streamObject } from 'ai' -import { getModel } from '@/lib/utils' +import { getModel, getReasoningProviderOptions } from '@/lib/utils' import { tavily } from '@tavily/core' import { resolutionSearchSchema } from '@/lib/schema/resolution-search' import { AI_REQUEST_TIMEOUT_MS, ENRICHMENT_TIMEOUT_MS, createDeadlineSignal, withTimeout } from '@/lib/utils/with-timeout' @@ -188,13 +188,15 @@ Analyze the user's prompt and the image to provide a holistic understanding of t ) // Use streamObject to get partial results. + const model = await getModel(hasImage) return withTimeout(Promise.resolve(streamObject({ - model: await getModel(hasImage), + model, system: systemPrompt, messages: filteredMessages, schema: resolutionSearchSchema, temperature: 0, maxTokens: 4096, + providerOptions: getReasoningProviderOptions(model), abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), })), AI_REQUEST_TIMEOUT_MS, 'Resolution analysis') } diff --git a/lib/utils/index.ts b/lib/utils/index.ts index b5801893..36c96d00 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -139,6 +139,18 @@ export async function getModel(requireVision: boolean = false) { return openai('gpt-5.6'); } +/** + * GPT-5 and o-series models share their output budget with hidden reasoning. + * Bound reasoning so streamed user-visible text is not starved. Detect the + * actual model because getModel can fall back to another provider. + */ +export function getReasoningProviderOptions(model: { modelId?: string }) { + const modelId = model?.modelId ?? '' + return modelId.startsWith('gpt-5') || modelId.startsWith('o') + ? { openai: { reasoningEffort: 'low' as const } } + : undefined +} + /** * Normalizes and sanitizes message content to plain text. */ From 2d5adebe41628ed2f620f4df773966b5638e725c Mon Sep 17 00:00:00 2001 From: ngoiyaeric <1.15367894e+08+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:18:12 +0000 Subject: [PATCH 4/6] fix: recover empty streamed responses --- lib/agents/researcher.tsx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index d72be6e0..8fbb81ee 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -206,8 +206,29 @@ export async function researcher( } } - if (toolResponses.length > 0 && !hasError && fullResponse.trim().length === 0) { + // Some reasoning-model/provider combinations finish with the final text + // available on result.text without emitting a text-delta event. Recover it + // before finalizing the stream so the response section cannot remain empty. + if (fullResponse.trim().length === 0) { + try { + const completedText = await result.text + if (completedText?.trim()) { + fullResponse = completedText + } + } catch (error) { + console.error('Unable to recover completed model text:', error) + } + } + + if (fullResponse.trim().length === 0 && toolResponses.length > 0 && !hasError) { fullResponse = 'Information gathered from search results.' + } + + if (fullResponse.trim().length === 0 && !hasError) { + fullResponse = 'The model returned no visible response. Please try again.' + } + + if (fullResponse.trim().length > 0) { if (!hasAppendedAnswerSection) { uiStream.append(answerSection) hasAppendedAnswerSection = true From 6459e7cb254545d38873952bcd4a9d2645b7a0d5 Mon Sep 17 00:00:00 2001 From: ngoiyaeric <1.15367894e+08+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:23:08 +0000 Subject: [PATCH 5/6] fix: use Responses API for GPT-5.6 tools --- lib/agents/researcher.tsx | 1 + lib/utils/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 8fbb81ee..4b59b2d3 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -201,6 +201,7 @@ export async function researcher( case 'error': hasError = true + console.error('Model response generation failed:', delta.error) fullResponse += `\n\nError: Model response generation failed.` break } diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 36c96d00..ca06e221 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -73,7 +73,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-5.6'); + return openai.responses('gpt-5.6'); } else { console.error('User selected "GPT-5.6" but OPENAI_API_KEY is not set.'); throw new Error('Selected model is not configured.'); @@ -87,7 +87,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-5.6'); + return openai.responses('gpt-5.6'); } catch (error) { console.warn('OpenAI API unavailable, falling back to next provider:', error); } @@ -136,7 +136,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai('gpt-5.6'); + return openai.responses('gpt-5.6'); } /** From 285985fc428a0ae2b0da9c0905a677a858020d7c Mon Sep 17 00:00:00 2001 From: ngoiyaeric <1.15367894e+08+ngoiyaeric@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:36:53 +0000 Subject: [PATCH 6/6] fix: align GPT-5 generation with API capabilities --- lib/agents/researcher.tsx | 30 ++++++++++++++++++++++++++++-- lib/agents/resolution-search.tsx | 26 +++++++++++++++++++++----- lib/utils/index.ts | 11 ++++++++--- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 4b59b2d3..87c032f6 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -6,11 +6,12 @@ import { ToolCallPart, ToolResultPart, streamText as nonexperimental_streamText, + generateText, } from 'ai' import { Section } from '@/components/section' import { BotMessage } from '@/components/message' import { getTools } from './tools' -import { getModel, getReasoningProviderOptions } from '../utils' +import { getModel, getReasoningProviderOptions, isNonStreamingModel } from '../utils' import { MapProvider } from '@/lib/store/settings' import { DrawnFeature } from './resolution-search' import { getSelectedModel } from '@/lib/actions/users' @@ -158,11 +159,36 @@ export async function researcher( }) const model = await getModel(hasImage) - const result = await nonexperimental_streamText({ + const generationOptions = { model: model as LanguageModel, maxTokens: 4096, temperature: 0, providerOptions: getReasoningProviderOptions(model), + maxSteps: 5, + abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), + system: systemPromptToUse, + messages, + tools: getTools({ uiStream, fullResponse, mapProvider, selectedModel, drawnFeatures }), + } + + if (isNonStreamingModel(model)) { + const generated = await generateText(generationOptions) + uiStream.update(null) + fullResponse = generated.text || '' + const generatedToolCalls = (generated.toolCalls || []) as ToolCallPart[] + const generatedToolResults = (generated.toolResults || []) as ToolResultPart[] + if (fullResponse.trim()) { + uiStream.append(answerSection) + streamText.update(fullResponse) + } + streamText.done(fullResponse) + messages.push({ role: 'assistant', content: [{ type: 'text', text: fullResponse }, ...generatedToolCalls] }) + if (generatedToolResults.length > 0) messages.push({ role: 'tool', content: generatedToolResults }) + return { result: generated, fullResponse, hasError: false, toolResponses: generatedToolResults } + } + + const result = await nonexperimental_streamText({ + ...generationOptions, // Allow multi-step tool calling (tool round + synthesis step with headroom for chained tool calls) maxSteps: 5, abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), diff --git a/lib/agents/resolution-search.tsx b/lib/agents/resolution-search.tsx index 1fc85edb..7fd89c04 100644 --- a/lib/agents/resolution-search.tsx +++ b/lib/agents/resolution-search.tsx @@ -1,5 +1,5 @@ -import { CoreMessage, streamObject } from 'ai' -import { getModel, getReasoningProviderOptions } from '@/lib/utils' +import { CoreMessage, generateObject, streamObject } from 'ai' +import { getModel, getReasoningProviderOptions, isNonStreamingModel } from '@/lib/utils' import { tavily } from '@tavily/core' import { resolutionSearchSchema } from '@/lib/schema/resolution-search' import { AI_REQUEST_TIMEOUT_MS, ENRICHMENT_TIMEOUT_MS, createDeadlineSignal, withTimeout } from '@/lib/utils/with-timeout' @@ -187,9 +187,11 @@ Analyze the user's prompt and the image to provide a holistic understanding of t message.content.some((part: any) => part.type === 'image') ) - // Use streamObject to get partial results. + // Use streamed output when the provider supports it. The configured GPT-5.5 + // endpoint is explicitly non-streaming, so use generateObject and expose the + // completed object through the same async interface consumed by actions.tsx. const model = await getModel(hasImage) - return withTimeout(Promise.resolve(streamObject({ + const generationOptions = { model, system: systemPrompt, messages: filteredMessages, @@ -198,5 +200,19 @@ Analyze the user's prompt and the image to provide a holistic understanding of t maxTokens: 4096, providerOptions: getReasoningProviderOptions(model), abortSignal: createDeadlineSignal(AI_REQUEST_TIMEOUT_MS), - })), AI_REQUEST_TIMEOUT_MS, 'Resolution analysis') + } + + if (isNonStreamingModel(model)) { + const generated = await generateObject(generationOptions) + return { + partialObjectStream: (async function* () { yield generated.object })(), + object: Promise.resolve(generated.object), + } + } + + return withTimeout( + Promise.resolve(streamObject(generationOptions)), + AI_REQUEST_TIMEOUT_MS, + 'Resolution analysis' + ) } diff --git a/lib/utils/index.ts b/lib/utils/index.ts index ca06e221..b497c6ae 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -73,7 +73,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai.responses('gpt-5.6'); + return openai.responses('gpt-5.5'); } else { console.error('User selected "GPT-5.6" but OPENAI_API_KEY is not set.'); throw new Error('Selected model is not configured.'); @@ -87,7 +87,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai.responses('gpt-5.6'); + return openai.responses('gpt-5.5'); } catch (error) { console.warn('OpenAI API unavailable, falling back to next provider:', error); } @@ -136,7 +136,7 @@ export async function getModel(requireVision: boolean = false) { const openai = createOpenAI({ apiKey: openaiApiKey, }); - return openai.responses('gpt-5.6'); + return openai.responses('gpt-5.5'); } /** @@ -151,6 +151,11 @@ export function getReasoningProviderOptions(model: { modelId?: string }) { : undefined } +/** The configured API advertises GPT-5.5 as non-streaming. */ +export function isNonStreamingModel(model: { modelId?: string }) { + return model?.modelId === 'gpt-5.5' +} + /** * Normalizes and sanitizes message content to plain text. */