mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix(antigravity): discover current source-control models and use CLI defaults
This commit is contained in:
@@ -59,6 +59,7 @@ export const getActionDescriptions = createLocalizedCatalog(
|
||||
const FALLBACK_AGENT_ARGS_PLACEHOLDER = '--model sonnet'
|
||||
|
||||
const AGENT_ARGS_PLACEHOLDER_OVERRIDES: Partial<Record<TuiAgent, string>> = {
|
||||
antigravity: '--effort low',
|
||||
// Why: Source Control AI action prompts are short, reviewable tasks; the
|
||||
// mini Codex model is a better default hint than the frontier model.
|
||||
codex: '--model gpt-5.4-mini',
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getDefaultSettings } from './constants'
|
||||
import { resolveSourceControlAiForOperation } from './source-control-ai'
|
||||
import { planCommitMessageGeneration } from './commit-message-plan'
|
||||
|
||||
describe('Antigravity source-control model selection', () => {
|
||||
for (const host of ['local', 'ssh:verification-host']) {
|
||||
for (const operation of ['commitMessage', 'branchName', 'pullRequest'] as const) {
|
||||
it(`${operation} on ${host} falls back from a retired saved model to the CLI configuration`, () => {
|
||||
const settings = getDefaultSettings('/tmp')
|
||||
settings.sourceControlAi = {
|
||||
...settings.sourceControlAi!,
|
||||
enabled: true,
|
||||
agentId: 'antigravity',
|
||||
selectedModelByAgent: { antigravity: 'Gemini 3.5 Flash (Medium)' },
|
||||
selectedModelByAgentByHost: {
|
||||
[host]: { antigravity: 'Gemini 3.5 Flash (Medium)' }
|
||||
},
|
||||
discoveredModelsByAgent: {},
|
||||
discoveredModelsByAgentByHost: {}
|
||||
}
|
||||
const result = resolveSourceControlAiForOperation({
|
||||
settings,
|
||||
repo: null,
|
||||
operation,
|
||||
discoveryHostKey: host
|
||||
})
|
||||
expect(result.ok).toBe(true)
|
||||
if (!result.ok) {
|
||||
throw new Error(result.error)
|
||||
}
|
||||
expect(result.value.params.model).toBe('default')
|
||||
const plan = planCommitMessageGeneration(result.value.params, 'Write Git text')
|
||||
expect(plan.ok).toBe(true)
|
||||
if (!plan.ok) {
|
||||
throw new Error(plan.error)
|
||||
}
|
||||
expect(plan.plan.args).toEqual(['--print=Write Git text', '--sandbox'])
|
||||
expect(plan.plan.stdinPayload).toBeNull()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
it('lets explicit recipe arguments override the discovered model and requested effort once', () => {
|
||||
const result = planCommitMessageGeneration(
|
||||
{
|
||||
agentId: 'antigravity',
|
||||
model: 'gemini-3.8-flash-medium',
|
||||
thinkingLevel: 'medium',
|
||||
agentArgs: '--model gemini-3.7-flash-low --effort low'
|
||||
},
|
||||
'Write Git text'
|
||||
)
|
||||
expect(result.ok).toBe(true)
|
||||
if (!result.ok) {
|
||||
throw new Error(result.error)
|
||||
}
|
||||
expect(result.plan.args.filter((arg) => arg === '--model')).toHaveLength(1)
|
||||
expect(result.plan.args.filter((arg) => arg === '--effort')).toHaveLength(1)
|
||||
expect(result.plan.args).not.toContain('gemini-3.8-flash-medium')
|
||||
expect(result.plan.args).not.toContain('medium')
|
||||
expect(result.plan.args).toContain('gemini-3.7-flash-low')
|
||||
expect(result.plan.args).toContain('low')
|
||||
})
|
||||
})
|
||||
@@ -607,7 +607,40 @@ describe('buildArgs (Antigravity)', () => {
|
||||
expect(spec.modelDiscovery?.args).toEqual(['models'])
|
||||
})
|
||||
|
||||
it('uses Gemini 3.5 Flash (Medium) as default model', () => {
|
||||
expect(COMMIT_MESSAGE_AGENT_SPECS.antigravity?.defaultModelId).toBe('Gemini 3.5 Flash (Medium)')
|
||||
it('uses the configured CLI model instead of a bundled model that can retire', () => {
|
||||
expect(spec.defaultModelId).toBe('default')
|
||||
expect(
|
||||
spec.buildArgs({ prompt: 'Generate a commit message', model: spec.defaultModelId })
|
||||
).toEqual(['--print=Generate a commit message', '--sandbox'])
|
||||
})
|
||||
|
||||
it('passes only a nonempty requested effort', () => {
|
||||
expect(spec.buildArgs({ prompt: 'P', model: 'default', thinkingLevel: '' })).not.toContain(
|
||||
'--effort'
|
||||
)
|
||||
expect(spec.buildArgs({ prompt: 'P', model: 'default', thinkingLevel: 'high' })).toEqual([
|
||||
'--print=P',
|
||||
'--sandbox',
|
||||
'--effort',
|
||||
'high'
|
||||
])
|
||||
})
|
||||
|
||||
it('parses current tab-separated IDs without treating progress text as a model', () => {
|
||||
expect(
|
||||
parseAntigravityModels(
|
||||
[
|
||||
'Fetching available models...',
|
||||
'id\tLabel',
|
||||
'gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)',
|
||||
'claude-sonnet-4-6\tClaude Sonnet 4.6 (Thinking)',
|
||||
'gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)',
|
||||
''
|
||||
].join('\r\n')
|
||||
)
|
||||
).toEqual([
|
||||
{ id: 'gemini-3.8-flash-medium', label: 'Gemini 3.8 Flash (Medium)' },
|
||||
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 (Thinking)' }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -216,15 +216,17 @@ export function buildSecondaryCommitMessageAgentSpecs({
|
||||
// using `--print=<value>` so a leading-dash prompt binds to the flag instead of
|
||||
// being parsed as its own option, and --sandbox/--model stay separate options.
|
||||
promptDelivery: 'argv',
|
||||
buildArgs: ({ prompt, model }) => [`--print=${prompt}`, '--sandbox', '--model', model],
|
||||
buildArgs: ({ prompt, model, thinkingLevel }) => [
|
||||
`--print=${prompt}`,
|
||||
'--sandbox',
|
||||
...(model && model !== 'default' ? ['--model', model] : []),
|
||||
...(thinkingLevel ? ['--effort', thinkingLevel] : [])
|
||||
],
|
||||
singletonOptions: [['--model'], ['--effort']],
|
||||
modelSource: 'dynamic',
|
||||
modelDiscovery: { binary: 'agy', args: ['models'], parse: parseAntigravityModels },
|
||||
models: [
|
||||
{ id: 'Gemini 3.5 Flash (Medium)', label: 'Gemini 3.5 Flash (Medium)' },
|
||||
{ id: 'Gemini 3.5 Flash (High)', label: 'Gemini 3.5 Flash (High)' },
|
||||
{ id: 'Gemini 3.5 Flash (Low)', label: 'Gemini 3.5 Flash (Low)' }
|
||||
],
|
||||
defaultModelId: 'Gemini 3.5 Flash (Medium)'
|
||||
models: [{ id: 'default', label: 'Config default' }],
|
||||
defaultModelId: 'default'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,13 +235,22 @@ export function parseCursorModels(stdout: string): CommitMessageModel[] {
|
||||
export function parseAntigravityModels(stdout: string): CommitMessageModel[] {
|
||||
const models: CommitMessageModel[] = []
|
||||
for (const rawLine of iterateModelOutputLines(stdout)) {
|
||||
const id = rawLine.trim()
|
||||
if (id.length === 0) {
|
||||
const line = rawLine.trim()
|
||||
const separator = line.indexOf('\t')
|
||||
const id = (separator === -1 ? line : line.slice(0, separator)).trim()
|
||||
const label = separator === -1 ? id : line.slice(separator + 1).trim()
|
||||
// Older agy versions list display names; current versions emit id<TAB>label.
|
||||
if (
|
||||
!id ||
|
||||
!label ||
|
||||
(separator === -1 && !/^.+ \((?:low|medium|high|thinking)\)$/i.test(id)) ||
|
||||
(separator !== -1 && (/\s/.test(id) || /^(?:id|model)$/i.test(id)))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
models.push({
|
||||
id,
|
||||
label: id
|
||||
label
|
||||
})
|
||||
}
|
||||
return uniqueModels(models)
|
||||
|
||||
Reference in New Issue
Block a user