Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "open-alice",
"version": "0.50.0-beta.1",
"version": "0.51.0-beta.1",
"description": "File-based trading agent engine",
"type": "module",
"main": "dist/electron/main.js",
Expand Down
20 changes: 6 additions & 14 deletions src/ai-providers/preset-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export const CLAUDE_OAUTH: PresetDef = {
}),
models: [
{ id: 'claude-opus-4-8', label: 'Claude Opus 4.8' },
{ id: 'claude-opus-4-7', label: 'Claude Opus 4.7' },
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6' },
],
}
Expand All @@ -84,7 +83,7 @@ export const CLAUDE_API: PresetDef = {
description: 'Pay per token via Anthropic API',
category: 'official',
defaultName: 'Claude (API Key)',
hint: 'Model is switchable here or from the profile list anytime. Opus is ~5× the cost of Sonnet; Haiku is cheapest for high-volume work.',
hint: 'Model is switchable here or from the profile list anytime. Opus is ~5× the cost of Sonnet.',
zodSchema: z.object({
backend: z.literal('agent-sdk'),
loginMethod: z.literal('api-key'),
Expand All @@ -93,9 +92,7 @@ export const CLAUDE_API: PresetDef = {
}),
models: [
{ id: 'claude-opus-4-8', label: 'Claude Opus 4.8' },
{ id: 'claude-opus-4-7', label: 'Claude Opus 4.7' },
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6' },
{ id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5' },
],
regions: [{ id: 'official', label: 'Official (api.anthropic.com)', wires: { anthropic: '' } }],
writeOnlyFields: ['apiKey'],
Expand All @@ -118,7 +115,6 @@ export const CODEX_OAUTH: PresetDef = {
models: [
{ id: 'gpt-5.5', label: 'GPT 5.5' },
{ id: 'gpt-5.4', label: 'GPT 5.4' },
{ id: 'gpt-5.4-mini', label: 'GPT 5.4 Mini' },
],
}

Expand All @@ -137,7 +133,6 @@ export const CODEX_API: PresetDef = {
models: [
{ id: 'gpt-5.5', label: 'GPT 5.5' },
{ id: 'gpt-5.4', label: 'GPT 5.4' },
{ id: 'gpt-5.4-mini', label: 'GPT 5.4 Mini' },
],
// Same key + base; the shape is how you call it. Responses is OpenAI's
// current API (what codex speaks); Chat Completions is the legacy shape
Expand Down Expand Up @@ -215,12 +210,12 @@ export const GLM: PresetDef = {
description: 'Zhipu GLM models via Claude Agent SDK (Anthropic-compatible)',
category: 'third-party',
defaultName: 'GLM',
hint: 'China console: bigmodel.cn — International console: z.ai. API keys are region-locked. GLM 5.1 is the current flagship, served on both regions.',
hint: 'China console: bigmodel.cn — International console: z.ai. API keys are region-locked. GLM 5.2 is the current flagship, served on both regions.',
zodSchema: z.object({
backend: z.literal('agent-sdk'),
loginMethod: z.literal('api-key'),
baseUrl: z.string().default('https://open.bigmodel.cn/api/anthropic').describe('API endpoint'),
model: z.string().default('glm-5.1').describe('Model'),
model: z.string().default('glm-5.2').describe('Model'),
apiKey: z.string().min(1).describe('GLM API key'),
}),
regions: [
Expand All @@ -232,9 +227,7 @@ export const GLM: PresetDef = {
} },
],
models: [
{ id: 'glm-5.1', label: 'GLM 5.1' },
{ id: 'glm-4.7', label: 'GLM 4.7' },
{ id: 'glm-4.5-air', label: 'GLM 4.5 Air' },
{ id: 'glm-5.2', label: 'GLM 5.2' },
],
writeOnlyFields: ['apiKey'],
}
Expand All @@ -257,7 +250,7 @@ export const KIMI: PresetDef = {
backend: z.literal('agent-sdk'),
loginMethod: z.literal('api-key'),
baseUrl: z.string().default('https://api.moonshot.cn/anthropic').describe('API endpoint'),
model: z.string().default('kimi-k2.6').describe('Model'),
model: z.string().default('kimi-k2.7-code').describe('Model'),
apiKey: z.string().min(1).describe('Moonshot API key'),
}),
regions: [
Expand All @@ -269,8 +262,8 @@ export const KIMI: PresetDef = {
} },
],
models: [
{ id: 'kimi-k2.7-code', label: 'Kimi K2.7 Code' },
{ id: 'kimi-k2.6', label: 'Kimi K2.6' },
{ id: 'kimi-k2.5', label: 'Kimi K2.5' },
],
writeOnlyFields: ['apiKey'],
}
Expand Down Expand Up @@ -298,7 +291,6 @@ export const DEEPSEEK: PresetDef = {
],
models: [
{ id: 'deepseek-v4-pro', label: 'DeepSeek V4 Pro (flagship)' },
{ id: 'deepseek-v4-flash', label: 'DeepSeek V4 Flash (cheap/fast)' },
],
writeOnlyFields: ['apiKey'],
}
Expand Down
45 changes: 35 additions & 10 deletions src/workspaces/agent-probe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,51 @@ export interface CodexProbeInput {
wireApi: 'chat' | 'responses';
}

/** Does this error mean the model MANDATES extended thinking? Some reasoning
* models (e.g. Kimi k2.7) 400 with "invalid thinking: only type=enabled is
* allowed for this model" when the request omits thinking — they can't run it
* disabled. We detect that to retry with thinking on, rather than enabling it
* for every model (Claude/GLM/etc. don't need it and some reject the param). */
function isThinkingRequiredError(err: unknown): boolean {
const e = err as { status?: number; message?: string } | undefined;
return e?.status === 400 && typeof e?.message === 'string' && /thinking/i.test(e.message);
}

export async function probeAnthropic(input: ClaudeProbeInput): Promise<ProbeResult> {
// `authToken` makes the SDK send `Authorization: Bearer`; `apiKey` makes it
// send `x-api-key`. Pick exactly one — sending both can trip gateways that
// reject ambiguous auth, and Anthropic's own API now 401s OAuth-via-Bearer.
const client = input.authMode === 'bearer'
? new Anthropic({ authToken: input.apiKey, baseURL: input.baseUrl })
: new Anthropic({ apiKey: input.apiKey, baseURL: input.baseUrl });
const msg = await client.messages.create({
model: input.model,
// Enough room for a reasoning model to finish thinking AND emit a visible
// reply on a trivial prompt — a tiny budget gets spent entirely on reasoning,
// leaving empty content (the "(empty reply)" the user saw). One-off per Test.
max_tokens: 512,
messages: [{ role: 'user', content: 'Hi' }],
});
const text = msg.content

const extract = (msg: Anthropic.Message): string => msg.content
.filter((b): b is Anthropic.TextBlock => b.type === 'text')
.map((b) => b.text)
.join('');
return { text };

try {
const msg = await client.messages.create({
model: input.model,
// Enough room for a reasoning model to finish thinking AND emit a visible
// reply on a trivial prompt — a tiny budget gets spent entirely on reasoning,
// leaving empty content (the "(empty reply)" the user saw). One-off per Test.
max_tokens: 512,
messages: [{ role: 'user', content: 'Hi' }],
});
return { text: extract(msg) };
} catch (err) {
if (!isThinkingRequiredError(err)) throw err;
// Thinking-mandatory model: retry with it enabled. budget_tokens must be
// < max_tokens and Anthropic's floor is 1024, so bump max_tokens to match.
const msg = await client.messages.create({
model: input.model,
max_tokens: 2048,
thinking: { type: 'enabled', budget_tokens: 1024 },
messages: [{ role: 'user', content: 'Hi' }],
});
return { text: extract(msg) };
}
}

export async function probeOpenAI(input: CodexProbeInput): Promise<ProbeResult> {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/workspace/WorkspaceAIConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ export function WorkspaceAIConfigModal({ wsId, onClose }: Props) {
value={form.model}
suggestions={modelSuggestions}
onChange={(v) => setForm({ ...form, model: v })}
placeholder={tab === 'claude' ? 'claude-sonnet-4-6' : tab === 'opencode' || tab === 'pi' ? 'deepseek-chat' : 'gpt-4o'}
placeholder={tab === 'claude' ? 'claude-opus-4-8' : tab === 'opencode' || tab === 'pi' ? 'deepseek-chat' : 'gpt-5.5'}
/>
{modelSuggestions.length > 0 && (
<p className="text-[11px] text-text-muted/70 mt-1">Suggestions from the matched provider — or type any model id.</p>
Expand Down
Loading