mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-30 19:49:14 +00:00
* fix(cycle): propose_takes skips cleanly when the chat provider is unavailable + narrow page projection Takeover of PR #1979 by @shawnduggan. The original PR gated on a hardcoded ANTHROPIC_API_KEY heuristic (modelNeedsAnthropicKey defaulting to true), which master deliberately removed elsewhere — it misclassified non-Anthropic stacks and fought the tier-config model resolution. This lands the intent the master-blessed way: probe the RESOLVED chat model (opts.model ?? getChatModel()) via probeChatModel — same semantics as patterns.ts / think/index.ts — and skip the phase cheaply when the provider can't run. Injected extractors are never gated. Also keeps the PR's uncontested half: load proposal candidates with a narrow projection (slug, source_id, compiled_truth) instead of listPages' SELECT p.*, preserving sourceIds > sourceId scope precedence and updated_desc ordering. Co-authored-by: shawnduggan <shawnduggan@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(brainstorm): price cost preview against the configured chat model + models.brainstorm.judge config key Takeover of PR #1855 by @starm2010, shrunk to the brainstorm-only portion (the cycle-phase hunks are superseded by the resolveModel-in- phase approach already on master). The cost preview + hard cost ceiling previously always priced anthropic:claude-sonnet-4-6 even when the configured chat_model (which the gateway actually runs) was something else; modelStr now resolves override → config.chat_model → fallback. The judge phase honors a new models.brainstorm.judge config key when no --judge-model flag is passed, resolved in the orchestrator so every caller (brainstorm, lsd, eval-brainstorm) benefits. Co-authored-by: starm2010 <starm2010@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): use withEnv()/emptyHome() in propose-takes no-key tests check:test-isolation R1 flagged direct process.env mutation in the two new no-key tests. Swap the hand-rolled save/mutate/restore for the canonical withEnv() helper (+ emptyHome() for the hermetic GBRAIN_HOME). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
/**
|
|
* Brainstorm model configurability (takeover of PR #1855 by @starm2010).
|
|
*
|
|
* - The cost preview + hard cost ceiling price the model that will actually
|
|
* run: --model override → configured chat_model → gateway fallback. Before
|
|
* this, the preview always priced anthropic:claude-sonnet-4-6 even when
|
|
* the configured chat_model was something else.
|
|
* - The judge phase honors the `models.brainstorm.judge` config key when no
|
|
* --judge-model flag is passed.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import {
|
|
resolveBrainstormChatModel,
|
|
resolveBrainstormJudgeModel,
|
|
} from '../../src/core/brainstorm/orchestrator.ts';
|
|
import type { BrainEngine } from '../../src/core/engine.ts';
|
|
|
|
function mockEngine(configValues: Record<string, string>): { engine: BrainEngine; reads: string[] } {
|
|
const reads: string[] = [];
|
|
const engine = {
|
|
async getConfig(key: string): Promise<string | null> {
|
|
reads.push(key);
|
|
return configValues[key] ?? null;
|
|
},
|
|
} as unknown as BrainEngine;
|
|
return { engine, reads };
|
|
}
|
|
|
|
describe('resolveBrainstormChatModel', () => {
|
|
test('--model override wins over config', () => {
|
|
expect(resolveBrainstormChatModel({ chat_model: 'openai:gpt-5' }, 'anthropic:claude-opus-4-6'))
|
|
.toBe('anthropic:claude-opus-4-6');
|
|
});
|
|
|
|
test('configured chat_model wins over the hardcoded fallback', () => {
|
|
expect(resolveBrainstormChatModel({ chat_model: 'openai:gpt-5' }))
|
|
.toBe('openai:gpt-5');
|
|
});
|
|
|
|
test('falls back to the gateway default model when nothing is configured', () => {
|
|
expect(resolveBrainstormChatModel({})).toBe('anthropic:claude-sonnet-4-6');
|
|
});
|
|
});
|
|
|
|
describe('resolveBrainstormJudgeModel', () => {
|
|
test('--judge-model flag wins without touching config', async () => {
|
|
const { engine, reads } = mockEngine({ 'models.brainstorm.judge': 'openai:gpt-5' });
|
|
const out = await resolveBrainstormJudgeModel(engine, 'anthropic:claude-opus-4-6');
|
|
expect(out).toBe('anthropic:claude-opus-4-6');
|
|
expect(reads).toHaveLength(0);
|
|
});
|
|
|
|
test('models.brainstorm.judge config key is honored when no flag is passed', async () => {
|
|
const { engine, reads } = mockEngine({ 'models.brainstorm.judge': 'openai:gpt-5' });
|
|
const out = await resolveBrainstormJudgeModel(engine);
|
|
expect(out).toBe('openai:gpt-5');
|
|
expect(reads).toEqual(['models.brainstorm.judge']);
|
|
});
|
|
|
|
test('returns undefined (defer to modelOverride / gateway default) when unset', async () => {
|
|
const { engine } = mockEngine({});
|
|
expect(await resolveBrainstormJudgeModel(engine)).toBeUndefined();
|
|
});
|
|
});
|