Skip to content
5 changes: 5 additions & 0 deletions apps/daemon/src/routes/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,10 @@ export function registerMemoryRoutes(app: Express, ctx: RegisterMemoryRoutesDeps
};
}
}
const chatModel =
typeof body.chatModel === 'string' && body.chatModel.trim()
? body.chatModel.trim()
: '';
let attemptedLLM = false;
if (userMessage.trim().length > 0 && hasAssistant) {
attemptedLLM = true;
Expand All @@ -600,6 +604,7 @@ export function registerMemoryRoutes(app: Express, ctx: RegisterMemoryRoutesDeps
projectRoot: PROJECT_ROOT,
chatAgentId: null,
chatProvider,
...(chatModel ? { chatModel } : {}),
},
),
)
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/ProjectView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,7 @@ function byokOpenCodeProviderFromConfig(
protocol: config.apiProtocol,
apiKey: config.apiKey.trim(),
baseUrl: config.baseUrl,
model: config.model,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field fixes the runtime wiring, but it also changes the BYOK provider payload shape that the web run-isolation tests lock down. The live Web workspace tests job on this head is failing in tests/components/ProjectView.run-isolation.test.tsx at the BYOK request assertions around lines 1531 and 1564: the actual calls now include byokProvider.model (llama3.2 / model), while the expected payloads still omit it. Because Validate workspace fails only through that web test failure, the PR cannot merge until the test contract matches this intentional shape change. Please update those expectations to include the selected model, or narrow the assertions if the exact nested provider object is not meant to be the contract.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

...(selectedProvider?.requiresApiKey === false ? { requiresApiKey: false } : {}),
apiVersion:
config.apiProtocol === 'azure'
Expand Down Expand Up @@ -5894,6 +5895,7 @@ export function ProjectView({
apiKey: byokOpenCodeProvider.apiKey,
baseUrl: byokOpenCodeProvider.baseUrl,
apiVersion: byokOpenCodeProvider.apiVersion,
model: byokOpenCodeProvider.model,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new field currently breaks the web typecheck because byokOpenCodeProvider is typed as ByokChatProviderConfig, and that shared contract only exposes protocol, apiKey, baseUrl, apiVersion, and requiresApiKey; it does not include model. CI is failing on this exact line with TS2339: Property 'model' does not exist on type 'ByokChatProviderConfig', so the PR cannot merge as-is even though the runtime intent is right. Please thread the selected chat model from a typed source, for example by adding an optional model field to ByokChatProviderConfig in packages/contracts/src/api/chat.ts and populating it from config.model in byokOpenCodeProviderFromConfig, or by building this memory-only snapshot directly from config.model where the request is made.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still does not actually send the selected BYOK chat model. The added field reads byokOpenCodeProvider.model, but byokOpenCodeProvider is created by byokOpenCodeProviderFromConfig() and that factory still returns only protocol, apiKey, baseUrl, requiresApiKey, and apiVersion; it never copies config.model. As a result this property serializes as undefined, memory.ts receives no chatProvider.model, and pickProvider() continues to choose envOverrideModel || explicitModel || defaults.model with explicitModel === '', falling back to gpt-4o-mini for OpenAI-compatible BYOK endpoints like MiniMax. That is the bug this PR is meant to fix. Please populate the provider snapshot from the active chat model, for example by adding model: config.model in byokOpenCodeProviderFromConfig() or by setting model: config.model directly in this memory-only byokChatProvider object.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now copies the selected model into the BYOK snapshot, but this snapshot still appears to be attached only to the pre-turn /api/memory/extract call. The request body just below sends userMessage, projectId, conversationId, and chatProvider, but no assistantMessage; in apps/daemon/src/routes/memory.ts, extractWithLLM() only runs when hasAssistant is true. The separate daemon close-handler path in apps/daemon/src/server.ts does have the assistant output, but it only passes chatAgentId: byok-opencode and chatModel; it does not pass this chatProvider, and chatProtocolFromAgentId() does not map byok-opencode to the user's actual BYOK provider/base URL.

That means the model value added here does not seem to reach the post-turn pickProvider() call that was falling back to gpt-4o-mini, so MiniMax/OpenAI-compatible BYOK memory extraction can still take the legacy env/media-config path instead of the user's BYOK endpoint. Please either send a post-turn /api/memory/extract request with both assistantMessage and the same byokChatProvider, or thread the BYOK provider snapshot into the daemon close-handler memoryOptions so pickProvider() receives the provider, base URL, API key, and selected model on the actual LLM extraction pass.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now includes the selected BYOK model in the /api/memory/extract snapshot, but this request still appears to run only the pre-turn heuristic path: the JSON body below sends userMessage and chatProvider, but no assistantMessage, while apps/daemon/src/routes/memory.ts only calls extractWithLLM() when hasAssistant is true. The actual post-turn extraction for byok-opencode is queued from the daemon close handler in apps/daemon/src/server.ts, and that path forwards chatAgentId/chatModel but not the byokProvider snapshot; chatProtocolFromAgentId() also does not map byok-opencode to the user's BYOK endpoint. So the model added here may still not reach the main LLM extraction pass that was falling back to gpt-4o-mini for MiniMax/OpenAI-compatible BYOK users.

Please either send a post-turn /api/memory/extract call with both assistantMessage and this same byokChatProvider, or thread the run-scoped byokProvider through the daemon close-handler memoryOptions as chatProvider so pickProvider() receives the provider, base URL, API key, and selected model on the extraction pass that has the assistant reply.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

}
: undefined;
if (userText.length > 0) {
Expand Down
2 changes: 2 additions & 0 deletions apps/web/tests/components/ProjectView.run-isolation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,7 @@ describe('ProjectView conversation run isolation', () => {
baseUrl: 'http://localhost:11434',
requiresApiKey: false,
apiVersion: '',
model: 'llama3.2',
},
model: 'llama3.2',
}));
Expand Down Expand Up @@ -1752,6 +1753,7 @@ describe('ProjectView conversation run isolation', () => {
baseUrl: 'http://127.0.0.1:8000/v1',
requiresApiKey: false,
apiVersion: '',
model: 'model',
},
model: 'model',
}));
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts/src/api/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ export interface ByokChatProviderConfig {
apiVersion?: string;
/** Explicit run-scoped provider policy for presets that do not require bearer credentials. */
requiresApiKey?: boolean;
/**
* Run-scoped chat model id selected in the chat UI. Forwarded to the daemon
* so BYOK-backed utilities (e.g. memory extraction) can honor the user's
* model picker instead of falling back to a hardcoded default. Optional
* because some presets (e.g. Ollama) infer the model from baseUrl/protocol.
*/
model?: string;
}

export interface ByokMediaDefaults {
Expand Down
Loading