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:
+1
-1
@@ -26,7 +26,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Sidebar: Recent rows show a compact timestamp on web and desktop, and pending permission/question badges are no longer covered by the hover actions (thanks to @makeittech).
|
- Sidebar: Recent rows show a compact timestamp on web and desktop, and pending permission/question badges are no longer covered by the hover actions (thanks to @makeittech).
|
||||||
- Mobile: Chats — sessions that belong to no project — now appear in the sessions sheet above the project list; opening an already-open agent switches to its editor instead of duplicating it (thanks to @bashrusakh); Android connections can trust user-installed certificate authorities, such as a local proxy's (thanks to @Silvenga).
|
- Mobile: Chats — sessions that belong to no project — now appear in the sessions sheet above the project list; opening an already-open agent switches to its editor instead of duplicating it (thanks to @bashrusakh); Android connections can trust user-installed certificate authorities, such as a local proxy's (thanks to @Silvenga).
|
||||||
- Settings: the editor font size survives a restart (thanks to @pascalandr); a change made right before closing the window is saved (thanks to @makeittech); number fields and selects no longer clip at large font sizes (thanks to @makeittech); refreshing GitHub account state no longer interrupts the page (thanks to @floze-the-genius); the Cloudflare Tunnel download link is fixed (thanks to @AyoubAchour); Windows skill paths are classified correctly, so disabled and duplicate skills are hidden as intended (thanks to @Ttungx).
|
- Settings: the editor font size survives a restart (thanks to @pascalandr); a change made right before closing the window is saved (thanks to @makeittech); number fields and selects no longer clip at large font sizes (thanks to @makeittech); refreshing GitHub account state no longer interrupts the page (thanks to @floze-the-genius); the Cloudflare Tunnel download link is fixed (thanks to @AyoubAchour); Windows skill paths are classified correctly, so disabled and duplicate skills are hidden as intended (thanks to @Ttungx).
|
||||||
- Small model: the model chosen in Settings now reaches the managed OpenCode process, so session titles use it (thanks to @makeittech); requests send the provider's configured headers, such as an API-gateway subscription key (thanks to @dmitrii-galantsev); a configured Anthropic endpoint is used without a doubled `/v1`, and Google models without reasoning no longer receive a thinking option (thanks to @mpeter and @IngTian).
|
- Small model: requests send the provider's configured headers, such as an API-gateway subscription key (thanks to @dmitrii-galantsev); a configured Anthropic endpoint is used without a doubled `/v1`, and Google models without reasoning no longer receive a thinking option (thanks to @mpeter and @IngTian).
|
||||||
- Projects: the folder picker can enter a directory that is already a project to browse from there (thanks to @weixiang1862), and sending, forking, and image attachments work in projects whose path has non-ASCII characters, such as `Masaüstü` (thanks to @fitzgpt).
|
- Projects: the folder picker can enter a directory that is already a project to browse from there (thanks to @weixiang1862), and sending, forking, and image attachments work in projects whose path has non-ASCII characters, such as `Masaüstü` (thanks to @fitzgpt).
|
||||||
- Git: the status panel refreshes from real repository state after checkout, branch, stash, merge, rebase, or reset, and remote branches that were never fetched appear in branch lists (thanks to @makeittech); the Branch diff scope no longer compares against the wrong base for branches created from the current branch (thanks to @gaojunran); picking `origin/main` in the branch selector checks out the local branch instead of a detached `HEAD` (thanks to @yulia-ivashko); branch search hides non-matching branches (thanks to @bashrusakh).
|
- Git: the status panel refreshes from real repository state after checkout, branch, stash, merge, rebase, or reset, and remote branches that were never fetched appear in branch lists (thanks to @makeittech); the Branch diff scope no longer compares against the wrong base for branches created from the current branch (thanks to @gaojunran); picking `origin/main` in the branch selector checks out the local branch instead of a detached `HEAD` (thanks to @yulia-ivashko); branch search hides non-matching branches (thanks to @bashrusakh).
|
||||||
- Updates: "Update OpenCode" no longer fails with a bare "Bad Request" — OpenChamber names the release to install and shows OpenCode's reason when refused — and the desktop "Restart to Update" button shows why an install failed, including an unsigned local build, and stays available to retry (thanks to @mdatsev and @yulia-ivashko).
|
- Updates: "Update OpenCode" no longer fails with a bare "Bad Request" — OpenChamber names the release to install and shows OpenCode's reason when refused — and the desktop "Restart to Update" button shows why an install failed, including an unsigned local build, and stays available to retry (thanks to @mdatsev and @yulia-ivashko).
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ import { configureOpenCodeRuntimeProviders, resetOpenCodeRuntimeProviders } from
|
|||||||
import { createOpenCodeWatcherRuntime } from './lib/opencode/watcher.js';
|
import { createOpenCodeWatcherRuntime } from './lib/opencode/watcher.js';
|
||||||
import { createSessionAssistRuntime } from './lib/session-assist/runtime.js';
|
import { createSessionAssistRuntime } from './lib/session-assist/runtime.js';
|
||||||
import { createSessionGoalRuntime } from './lib/session-goal/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 { createContextObligatoryRuntime } from './lib/context-obligatory/runtime.js';
|
||||||
import { createSessionKnowledgeRuntime } from './lib/session-knowledge/runtime.js';
|
import { createSessionKnowledgeRuntime } from './lib/session-knowledge/runtime.js';
|
||||||
import { createScheduledTasksRuntime } from './lib/scheduled-tasks/runtime.js';
|
import { createScheduledTasksRuntime } from './lib/scheduled-tasks/runtime.js';
|
||||||
@@ -1205,25 +1204,11 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
|
|||||||
const managedEnv = includeControl || includeWeb || includeMemory
|
const managedEnv = includeControl || includeWeb || includeMemory
|
||||||
? await (agentToolRuntime?.prepareManagedOpenCodeEnv({ includeControl, includeWeb, includeMemory }) || {})
|
? await (agentToolRuntime?.prepareManagedOpenCodeEnv({ includeControl, includeWeb, includeMemory }) || {})
|
||||||
: {};
|
: {};
|
||||||
const envWithSystemPrompt = settings?.optimizeSystemPrompt === true
|
if (settings?.optimizeSystemPrompt !== true) return managedEnv;
|
||||||
? {
|
|
||||||
...managedEnv,
|
|
||||||
...(await systemPromptRuntime.prepareManagedOpenCodeEnv(
|
|
||||||
managedEnv.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
: managedEnv;
|
|
||||||
|
|
||||||
// Apply the explicit Small Model override to the managed OpenCode config
|
const configContent = managedEnv.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT;
|
||||||
// so OpenCode's own title/summary generation uses the user's chosen model.
|
const systemPromptEnv = await systemPromptRuntime.prepareManagedOpenCodeEnv(configContent);
|
||||||
const configContent = envWithSystemPrompt.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT;
|
return { ...managedEnv, ...systemPromptEnv };
|
||||||
const withSmallModel = applySmallModelOverrideToOpenCodeConfig({
|
|
||||||
configContent,
|
|
||||||
smallModelUseDefault: settings?.smallModelUseDefault,
|
|
||||||
smallModelOverride: settings?.smallModelOverride,
|
|
||||||
});
|
|
||||||
if (withSmallModel === configContent) return envWithSystemPrompt;
|
|
||||||
return { ...envWithSystemPrompt, OPENCODE_CONFIG_CONTENT: withSmallModel };
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -137,20 +137,6 @@ other runtime API.
|
|||||||
- `routes.js` — `GET /api/small-model` (resolution preview) and
|
- `routes.js` — `GET /api/small-model` (resolution preview) and
|
||||||
`POST /api/small-model/generate` (`{ prompt, system?, maxOutputTokens?,
|
`POST /api/small-model/generate` (`{ prompt, system?, maxOutputTokens?,
|
||||||
model?, directory? }` → `{ text, providerID, modelID, source }`).
|
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
|
## 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