revert: stop forwarding the Small Model override into the managed OpenCode config

Reverts #2687. In real use the injected small_model behaves poorly with
OpenCode: its internal small-model consumers and OpenChamber's own small
model are different things and must stay configured separately.
This commit is contained in:
Bohdan Triapitsyn
2026-08-29 01:14:02 +03:00
parent 48bcac1758
commit 8aba30ac43
5 changed files with 5 additions and 175 deletions
+4 -19
View File
@@ -76,7 +76,6 @@ import { configureOpenCodeRuntimeProviders, resetOpenCodeRuntimeProviders } from
import { createOpenCodeWatcherRuntime } from './lib/opencode/watcher.js';
import { createSessionAssistRuntime } from './lib/session-assist/runtime.js';
import { createSessionGoalRuntime } from './lib/session-goal/runtime.js';
import { applySmallModelOverrideToOpenCodeConfig } from './lib/small-model/config-injection.js';
import { createContextObligatoryRuntime } from './lib/context-obligatory/runtime.js';
import { createSessionKnowledgeRuntime } from './lib/session-knowledge/runtime.js';
import { createScheduledTasksRuntime } from './lib/scheduled-tasks/runtime.js';
@@ -1205,25 +1204,11 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
const managedEnv = includeControl || includeWeb || includeMemory
? await (agentToolRuntime?.prepareManagedOpenCodeEnv({ includeControl, includeWeb, includeMemory }) || {})
: {};
const envWithSystemPrompt = settings?.optimizeSystemPrompt === true
? {
...managedEnv,
...(await systemPromptRuntime.prepareManagedOpenCodeEnv(
managedEnv.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT,
)),
}
: managedEnv;
if (settings?.optimizeSystemPrompt !== true) return managedEnv;
// Apply the explicit Small Model override to the managed OpenCode config
// so OpenCode's own title/summary generation uses the user's chosen model.
const configContent = envWithSystemPrompt.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT;
const withSmallModel = applySmallModelOverrideToOpenCodeConfig({
configContent,
smallModelUseDefault: settings?.smallModelUseDefault,
smallModelOverride: settings?.smallModelOverride,
});
if (withSmallModel === configContent) return envWithSystemPrompt;
return { ...envWithSystemPrompt, OPENCODE_CONFIG_CONTENT: withSmallModel };
const configContent = managedEnv.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT;
const systemPromptEnv = await systemPromptRuntime.prepareManagedOpenCodeEnv(configContent);
return { ...managedEnv, ...systemPromptEnv };
},
});
@@ -137,20 +137,6 @@ other runtime API.
- `routes.js``GET /api/small-model` (resolution preview) and
`POST /api/small-model/generate` (`{ prompt, system?, maxOutputTokens?,
model?, directory? }` → `{ text, providerID, modelID, source }`).
- `config-injection.js` — applies the Settings → Chat → Small Model override
to the config injected into the **managed OpenCode process**
(`OPENCODE_CONFIG_CONTENT`), so OpenCode's own internal `small_model`
consumers — session title and summary generation — use the user's explicit
choice instead of OpenCode's fallback chain. Only an explicit override
(`smallModelUseDefault === false` with a non-empty `smallModelOverride`) is
injected; "use default" leaves the config untouched so OpenCode's own
resolution stays authoritative. Wired into `getManagedOpenCodeEnv` in
`server/index.js`; the pure helper is unit-tested in
`config-injection.test.js`. External OpenCode servers are unaffected (they
are not launched with this env). The injected `small_model` is baked into
`OPENCODE_CONFIG_CONTENT` when the managed process spawns, so changing the
override in Settings applies on the next managed OpenCode restart, not to
the process already running.
## Which providers the pickers may offer
@@ -1,51 +0,0 @@
/**
* Applies the user's explicit Small Model override (Settings Chat Small
* Model) to the configuration injected into the managed OpenCode process.
*
* OpenCode's own session-title and summary generation reads `small_model`
* from its config layers. Previously the OpenChamber settings override only
* fed OpenChamber's own `/api/small-model/generate` utility service, so a
* configured Small Model never reached OpenCode's title generation and
* sessions kept their fallback/untitled state. Injecting the override as
* `small_model` in the managed `OPENCODE_CONFIG_CONTENT` closes that gap for
* the managed server.
*
* Only an explicit override applies (`smallModelUseDefault === false` with a
* non-empty `smallModelOverride`). "Use default" leaves the config untouched,
* so OpenCode's own resolution chain (config `small_model`, then its family
* scan) stays authoritative this mirrors the precedence documented in
* `packages/web/server/lib/small-model/DOCUMENTATION.md`.
*
* Malformed user config is left untouched rather than rewritten: OpenCode's
* own loader is the right place to surface it, and silently rewriting it
* would hide the error.
*/
export const applySmallModelOverrideToOpenCodeConfig = ({
configContent,
smallModelUseDefault,
smallModelOverride,
}) => {
if (smallModelUseDefault !== false) {
return configContent;
}
const override = typeof smallModelOverride === 'string' ? smallModelOverride.trim() : '';
if (!override) {
return configContent;
}
const current = (() => {
if (typeof configContent !== 'string' || configContent.trim().length === 0) {
return {};
}
try {
return JSON.parse(configContent);
} catch {
return null;
}
})();
if (current === null || typeof current !== 'object' || Array.isArray(current)) {
return configContent;
}
return JSON.stringify({ ...current, small_model: override });
};
@@ -1,90 +0,0 @@
import { describe, expect, it } from 'vitest';
import { applySmallModelOverrideToOpenCodeConfig } from './config-injection.js';
describe('applySmallModelOverrideToOpenCodeConfig', () => {
it('leaves config unchanged when use-default is not explicitly disabled', () => {
const config = '{"model":"anthropic/claude-sonnet-4-5"}';
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: config,
smallModelUseDefault: true,
smallModelOverride: 'anthropic/claude-haiku-4-5',
}),
).toBe(config);
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: config,
smallModelUseDefault: undefined,
smallModelOverride: 'anthropic/claude-haiku-4-5',
}),
).toBe(config);
});
it('leaves config unchanged when the override is empty or whitespace', () => {
const config = '{"model":"anthropic/claude-sonnet-4-5"}';
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: config,
smallModelUseDefault: false,
smallModelOverride: ' ',
}),
).toBe(config);
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: config,
smallModelUseDefault: false,
smallModelOverride: undefined,
}),
).toBe(config);
});
it('injects small_model into an empty config', () => {
const result = applySmallModelOverrideToOpenCodeConfig({
configContent: undefined,
smallModelUseDefault: false,
smallModelOverride: 'anthropic/claude-haiku-4-5',
});
expect(JSON.parse(result)).toEqual({ small_model: 'anthropic/claude-haiku-4-5' });
});
it('injects small_model while preserving existing config keys and plugins', () => {
const result = applySmallModelOverrideToOpenCodeConfig({
configContent: '{"model":"anthropic/claude-sonnet-4-5","plugin":["file:///tool.js"]}',
smallModelUseDefault: false,
smallModelOverride: 'google/gemini-2.5-flash',
});
expect(JSON.parse(result)).toEqual({
model: 'anthropic/claude-sonnet-4-5',
plugin: ['file:///tool.js'],
small_model: 'google/gemini-2.5-flash',
});
});
it('replaces an existing small_model with the override', () => {
const result = applySmallModelOverrideToOpenCodeConfig({
configContent: '{"small_model":"anthropic/claude-haiku-4-5"}',
smallModelUseDefault: false,
smallModelOverride: 'google/gemini-2.5-flash',
});
expect(JSON.parse(result)).toEqual({ small_model: 'google/gemini-2.5-flash' });
});
it('leaves malformed config untouched instead of rewriting it', () => {
const config = '{not-valid-json';
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: config,
smallModelUseDefault: false,
smallModelOverride: 'anthropic/claude-haiku-4-5',
}),
).toBe(config);
const arrayConfig = '["not","an","object"]';
expect(
applySmallModelOverrideToOpenCodeConfig({
configContent: arrayConfig,
smallModelUseDefault: false,
smallModelOverride: 'anthropic/claude-haiku-4-5',
}),
).toBe(arrayConfig);
});
});