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:
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user