@agi-cli/server 0.1.119 → 0.1.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/index.ts +9 -5
- package/src/openapi/paths/git.ts +4 -0
- package/src/routes/ask.ts +13 -14
- package/src/routes/branch.ts +106 -0
- package/src/routes/config/agents.ts +1 -1
- package/src/routes/config/cwd.ts +1 -1
- package/src/routes/config/main.ts +1 -1
- package/src/routes/config/models.ts +32 -4
- package/src/routes/config/providers.ts +1 -1
- package/src/routes/config/utils.ts +14 -1
- package/src/routes/files.ts +1 -1
- package/src/routes/git/commit.ts +23 -6
- package/src/routes/git/schemas.ts +1 -0
- package/src/routes/session-files.ts +1 -1
- package/src/routes/session-messages.ts +2 -2
- package/src/routes/sessions.ts +8 -6
- package/src/runtime/agent/registry.ts +333 -0
- package/src/runtime/agent/runner-reasoning.ts +108 -0
- package/src/runtime/agent/runner-setup.ts +265 -0
- package/src/runtime/agent/runner.ts +356 -0
- package/src/runtime/agent-registry.ts +6 -333
- package/src/runtime/{ask-service.ts → ask/service.ts} +5 -5
- package/src/runtime/{debug.ts → debug/index.ts} +1 -1
- package/src/runtime/{api-error.ts → errors/api-error.ts} +2 -2
- package/src/runtime/message/compaction-auto.ts +137 -0
- package/src/runtime/message/compaction-context.ts +64 -0
- package/src/runtime/message/compaction-detect.ts +19 -0
- package/src/runtime/message/compaction-limits.ts +58 -0
- package/src/runtime/message/compaction-mark.ts +115 -0
- package/src/runtime/message/compaction-prune.ts +75 -0
- package/src/runtime/message/compaction.ts +23 -0
- package/src/runtime/{history-builder.ts → message/history-builder.ts} +2 -2
- package/src/runtime/{message-service.ts → message/service.ts} +8 -14
- package/src/runtime/{history → message}/tool-history-tracker.ts +1 -1
- package/src/runtime/{prompt.ts → prompt/builder.ts} +1 -1
- package/src/runtime/{provider.ts → provider/anthropic.ts} +4 -219
- package/src/runtime/provider/google.ts +12 -0
- package/src/runtime/provider/index.ts +44 -0
- package/src/runtime/provider/openai.ts +26 -0
- package/src/runtime/provider/opencode.ts +61 -0
- package/src/runtime/provider/openrouter.ts +11 -0
- package/src/runtime/provider/solforge.ts +22 -0
- package/src/runtime/provider/zai.ts +53 -0
- package/src/runtime/session/branch.ts +277 -0
- package/src/runtime/{db-operations.ts → session/db-operations.ts} +1 -1
- package/src/runtime/{session-manager.ts → session/manager.ts} +1 -1
- package/src/runtime/{session-queue.ts → session/queue.ts} +2 -2
- package/src/runtime/stream/abort-handler.ts +65 -0
- package/src/runtime/stream/error-handler.ts +200 -0
- package/src/runtime/stream/finish-handler.ts +123 -0
- package/src/runtime/stream/handlers.ts +5 -0
- package/src/runtime/stream/step-finish.ts +93 -0
- package/src/runtime/stream/types.ts +17 -0
- package/src/runtime/{tool-context.ts → tools/context.ts} +1 -1
- package/src/runtime/{tool-context-setup.ts → tools/setup.ts} +3 -3
- package/src/runtime/{token-utils.ts → utils/token.ts} +2 -2
- package/src/tools/adapter.ts +4 -4
- package/src/runtime/compaction.ts +0 -536
- package/src/runtime/runner.ts +0 -654
- package/src/runtime/stream-handlers.ts +0 -508
- /package/src/runtime/{cache-optimizer.ts → context/cache-optimizer.ts} +0 -0
- /package/src/runtime/{environment.ts → context/environment.ts} +0 -0
- /package/src/runtime/{context-optimizer.ts → context/optimizer.ts} +0 -0
- /package/src/runtime/{debug-state.ts → debug/state.ts} +0 -0
- /package/src/runtime/{error-handling.ts → errors/handling.ts} +0 -0
- /package/src/runtime/{history-truncator.ts → message/history-truncator.ts} +0 -0
- /package/src/runtime/{provider-selection.ts → provider/selection.ts} +0 -0
- /package/src/runtime/{tool-mapping.ts → tools/mapping.ts} +0 -0
- /package/src/runtime/{cwd.ts → utils/cwd.ts} +0 -0
|
@@ -1,80 +1,11 @@
|
|
|
1
|
-
import type { AGIConfig
|
|
2
|
-
import {
|
|
3
|
-
catalog,
|
|
4
|
-
createSolforgeModel,
|
|
5
|
-
createOpenAIOAuthModel,
|
|
6
|
-
getAuth,
|
|
7
|
-
refreshToken,
|
|
8
|
-
setAuth,
|
|
9
|
-
} from '@agi-cli/sdk';
|
|
10
|
-
import { openai, createOpenAI } from '@ai-sdk/openai';
|
|
1
|
+
import type { AGIConfig } from '@agi-cli/sdk';
|
|
2
|
+
import { getAuth, refreshToken, setAuth } from '@agi-cli/sdk';
|
|
11
3
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
12
|
-
import {
|
|
13
|
-
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
14
|
-
import { toClaudeCodeName } from './tool-mapping.ts';
|
|
4
|
+
import { toClaudeCodeName } from '../tools/mapping.ts';
|
|
15
5
|
|
|
16
|
-
// Version to report in user-agent for Claude Code compatibility
|
|
17
6
|
const CLAUDE_CLI_VERSION = '1.0.61';
|
|
18
|
-
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
19
7
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
async function getZaiInstance(cfg: AGIConfig, model: string) {
|
|
23
|
-
const auth = await getAuth('zai', cfg.projectRoot);
|
|
24
|
-
const entry = catalog.zai;
|
|
25
|
-
|
|
26
|
-
let apiKey = '';
|
|
27
|
-
const baseURL = entry?.api || 'https://api.z.ai/api/paas/v4';
|
|
28
|
-
|
|
29
|
-
if (auth?.type === 'api' && auth.key) {
|
|
30
|
-
apiKey = auth.key;
|
|
31
|
-
} else {
|
|
32
|
-
apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
36
|
-
|
|
37
|
-
const instance = createOpenAICompatible({
|
|
38
|
-
name: entry?.label ?? 'Z.AI',
|
|
39
|
-
baseURL,
|
|
40
|
-
headers,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return instance(model);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function getZaiCodingInstance(cfg: AGIConfig, model: string) {
|
|
47
|
-
const auth =
|
|
48
|
-
(await getAuth('zai', cfg.projectRoot)) ||
|
|
49
|
-
(await getAuth('zai-coding', cfg.projectRoot));
|
|
50
|
-
const entry = catalog['zai-coding'];
|
|
51
|
-
|
|
52
|
-
let apiKey = '';
|
|
53
|
-
const baseURL = entry?.api || 'https://api.z.ai/api/coding/paas/v4';
|
|
54
|
-
|
|
55
|
-
if (auth?.type === 'api' && auth.key) {
|
|
56
|
-
apiKey = auth.key;
|
|
57
|
-
} else {
|
|
58
|
-
apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
62
|
-
|
|
63
|
-
const instance = createOpenAICompatible({
|
|
64
|
-
name: entry?.label ?? 'Z.AI Coding',
|
|
65
|
-
baseURL,
|
|
66
|
-
headers,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return instance(model);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getOpenRouterInstance() {
|
|
73
|
-
const apiKey = process.env.OPENROUTER_API_KEY ?? '';
|
|
74
|
-
return createOpenRouter({ apiKey });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function getAnthropicInstance(cfg: AGIConfig) {
|
|
8
|
+
export async function getAnthropicInstance(cfg: AGIConfig) {
|
|
78
9
|
const auth = await getAuth('anthropic', cfg.projectRoot);
|
|
79
10
|
|
|
80
11
|
if (auth?.type === 'oauth') {
|
|
@@ -137,7 +68,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
137
68
|
}
|
|
138
69
|
}
|
|
139
70
|
|
|
140
|
-
// Required Claude Code headers
|
|
141
71
|
headers.authorization = `Bearer ${currentAuth.access}`;
|
|
142
72
|
headers['anthropic-beta'] =
|
|
143
73
|
'claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14';
|
|
@@ -149,7 +79,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
149
79
|
headers['content-type'] = 'application/json';
|
|
150
80
|
headers.accept = 'application/json';
|
|
151
81
|
|
|
152
|
-
// Stainless headers (fingerprinting)
|
|
153
82
|
headers['x-stainless-arch'] = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
154
83
|
headers['x-stainless-helper-method'] = 'stream';
|
|
155
84
|
headers['x-stainless-lang'] = 'js';
|
|
@@ -165,19 +94,16 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
165
94
|
headers['x-stainless-runtime-version'] = process.version;
|
|
166
95
|
headers['x-stainless-timeout'] = '600';
|
|
167
96
|
|
|
168
|
-
// Add ?beta=true to URL
|
|
169
97
|
let url = typeof input === 'string' ? input : input.toString();
|
|
170
98
|
if (url.includes('/v1/messages') && !url.includes('beta=true')) {
|
|
171
99
|
url += url.includes('?') ? '&beta=true' : '?beta=true';
|
|
172
100
|
}
|
|
173
101
|
|
|
174
|
-
// Transform request body: tool names to PascalCase + apply caching
|
|
175
102
|
let body = init?.body;
|
|
176
103
|
if (body && typeof body === 'string') {
|
|
177
104
|
try {
|
|
178
105
|
const parsed = JSON.parse(body);
|
|
179
106
|
|
|
180
|
-
// Transform tool names
|
|
181
107
|
if (parsed.tools && Array.isArray(parsed.tools)) {
|
|
182
108
|
parsed.tools = parsed.tools.map(
|
|
183
109
|
(tool: { name: string; [key: string]: unknown }) => ({
|
|
@@ -187,16 +113,11 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
187
113
|
);
|
|
188
114
|
}
|
|
189
115
|
|
|
190
|
-
// Apply ephemeral caching (max 4 cache breakpoints total)
|
|
191
|
-
// Adapter adds 2 tool cache blocks, so we can add 2 more:
|
|
192
|
-
// - 1 system block (the first one with tools description)
|
|
193
|
-
// - 1 message block (the last user message)
|
|
194
116
|
const MAX_SYSTEM_CACHE = 1;
|
|
195
117
|
const MAX_MESSAGE_CACHE = 1;
|
|
196
118
|
let systemCacheUsed = 0;
|
|
197
119
|
let messageCacheUsed = 0;
|
|
198
120
|
|
|
199
|
-
// Cache first system message only (contains agent instructions)
|
|
200
121
|
if (parsed.system && Array.isArray(parsed.system)) {
|
|
201
122
|
parsed.system = parsed.system.map(
|
|
202
123
|
(
|
|
@@ -217,7 +138,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
217
138
|
);
|
|
218
139
|
}
|
|
219
140
|
|
|
220
|
-
// Transform tool names in messages and apply caching to last message only
|
|
221
141
|
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
222
142
|
const messageCount = parsed.messages.length;
|
|
223
143
|
|
|
@@ -230,7 +150,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
230
150
|
},
|
|
231
151
|
msgIndex: number,
|
|
232
152
|
) => {
|
|
233
|
-
// Only cache the very last message
|
|
234
153
|
const isLast = msgIndex === messageCount - 1;
|
|
235
154
|
|
|
236
155
|
if (Array.isArray(msg.content)) {
|
|
@@ -245,7 +164,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
245
164
|
) => {
|
|
246
165
|
let transformedBlock = block;
|
|
247
166
|
|
|
248
|
-
// Transform tool names
|
|
249
167
|
if (block.type === 'tool_use' && block.name) {
|
|
250
168
|
transformedBlock = {
|
|
251
169
|
...block,
|
|
@@ -259,7 +177,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
259
177
|
};
|
|
260
178
|
}
|
|
261
179
|
|
|
262
|
-
// Add cache_control to last block of last message
|
|
263
180
|
if (
|
|
264
181
|
isLast &&
|
|
265
182
|
!transformedBlock.cache_control &&
|
|
@@ -279,7 +196,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
279
196
|
return { ...msg, content };
|
|
280
197
|
}
|
|
281
198
|
|
|
282
|
-
// For string content, wrap in array with cache_control if last message
|
|
283
199
|
if (
|
|
284
200
|
isLast &&
|
|
285
201
|
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
@@ -321,8 +237,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
321
237
|
});
|
|
322
238
|
}
|
|
323
239
|
|
|
324
|
-
// For API key auth, also apply caching via customFetch
|
|
325
|
-
// This optimizes token usage even without OAuth
|
|
326
240
|
const customFetch = async (
|
|
327
241
|
input: string | URL | Request,
|
|
328
242
|
init?: RequestInit,
|
|
@@ -332,15 +246,11 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
332
246
|
try {
|
|
333
247
|
const parsed = JSON.parse(body);
|
|
334
248
|
|
|
335
|
-
// Apply ephemeral caching (max 4 cache breakpoints total)
|
|
336
|
-
// Adapter adds 2 tool cache blocks, so we can add 2 more:
|
|
337
|
-
// - 1 system block + 1 message block = 2
|
|
338
249
|
const MAX_SYSTEM_CACHE = 1;
|
|
339
250
|
const MAX_MESSAGE_CACHE = 1;
|
|
340
251
|
let systemCacheUsed = 0;
|
|
341
252
|
let messageCacheUsed = 0;
|
|
342
253
|
|
|
343
|
-
// Cache first system message
|
|
344
254
|
if (parsed.system && Array.isArray(parsed.system)) {
|
|
345
255
|
parsed.system = parsed.system.map(
|
|
346
256
|
(
|
|
@@ -361,7 +271,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
361
271
|
);
|
|
362
272
|
}
|
|
363
273
|
|
|
364
|
-
// Cache last message only
|
|
365
274
|
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
366
275
|
const messageCount = parsed.messages.length;
|
|
367
276
|
parsed.messages = parsed.messages.map(
|
|
@@ -432,127 +341,3 @@ async function getAnthropicInstance(cfg: AGIConfig) {
|
|
|
432
341
|
fetch: customFetch as typeof fetch,
|
|
433
342
|
});
|
|
434
343
|
}
|
|
435
|
-
|
|
436
|
-
export async function resolveModel(
|
|
437
|
-
provider: ProviderName,
|
|
438
|
-
model: string,
|
|
439
|
-
cfg: AGIConfig,
|
|
440
|
-
options?: { systemPrompt?: string },
|
|
441
|
-
) {
|
|
442
|
-
if (provider === 'openai') {
|
|
443
|
-
const auth = await getAuth('openai', cfg.projectRoot);
|
|
444
|
-
if (auth?.type === 'oauth') {
|
|
445
|
-
const isCodexModel = model.toLowerCase().includes('codex');
|
|
446
|
-
return createOpenAIOAuthModel(model, {
|
|
447
|
-
oauth: auth,
|
|
448
|
-
projectRoot: cfg.projectRoot,
|
|
449
|
-
reasoningEffort: isCodexModel ? 'high' : 'medium',
|
|
450
|
-
reasoningSummary: 'auto',
|
|
451
|
-
instructions: options?.systemPrompt,
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
if (auth?.type === 'api' && auth.key) {
|
|
455
|
-
const instance = createOpenAI({ apiKey: auth.key });
|
|
456
|
-
return instance(model);
|
|
457
|
-
}
|
|
458
|
-
return openai(model);
|
|
459
|
-
}
|
|
460
|
-
if (provider === 'anthropic') {
|
|
461
|
-
const instance = await getAnthropicInstance(cfg);
|
|
462
|
-
return instance(model);
|
|
463
|
-
}
|
|
464
|
-
if (provider === 'google') {
|
|
465
|
-
const auth = await getAuth('google', cfg.projectRoot);
|
|
466
|
-
if (auth?.type === 'api' && auth.key) {
|
|
467
|
-
const instance = createGoogleGenerativeAI({ apiKey: auth.key });
|
|
468
|
-
return instance(model);
|
|
469
|
-
}
|
|
470
|
-
return google(model);
|
|
471
|
-
}
|
|
472
|
-
if (provider === 'openrouter') {
|
|
473
|
-
const openrouter = getOpenRouterInstance();
|
|
474
|
-
return openrouter.chat(model);
|
|
475
|
-
}
|
|
476
|
-
if (provider === 'opencode') {
|
|
477
|
-
const entry = catalog[provider];
|
|
478
|
-
const normalizedModel = normalizeModelIdentifier(provider, model);
|
|
479
|
-
const modelInfo =
|
|
480
|
-
entry?.models.find((m) => m.id === normalizedModel) ??
|
|
481
|
-
entry?.models.find((m) => m.id === model);
|
|
482
|
-
const resolvedModelId = modelInfo?.id ?? normalizedModel ?? model;
|
|
483
|
-
const binding = modelInfo?.provider?.npm ?? entry?.npm;
|
|
484
|
-
const apiKey = process.env.OPENCODE_API_KEY ?? '';
|
|
485
|
-
const baseURL =
|
|
486
|
-
modelInfo?.provider?.baseURL ||
|
|
487
|
-
modelInfo?.provider?.api ||
|
|
488
|
-
entry?.api ||
|
|
489
|
-
'https://opencode.ai/zen/v1';
|
|
490
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
491
|
-
if (binding === '@ai-sdk/openai') {
|
|
492
|
-
const instance = createOpenAI({ apiKey, baseURL });
|
|
493
|
-
return instance(resolvedModelId);
|
|
494
|
-
}
|
|
495
|
-
if (binding === '@ai-sdk/anthropic') {
|
|
496
|
-
const instance = createAnthropic({ apiKey, baseURL });
|
|
497
|
-
return instance(resolvedModelId);
|
|
498
|
-
}
|
|
499
|
-
if (binding === '@ai-sdk/openai-compatible') {
|
|
500
|
-
const instance = createOpenAICompatible({
|
|
501
|
-
name: entry?.label ?? 'opencode',
|
|
502
|
-
baseURL,
|
|
503
|
-
headers,
|
|
504
|
-
});
|
|
505
|
-
return instance(resolvedModelId);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const ocOpenAI = createOpenAI({ apiKey, baseURL });
|
|
509
|
-
const ocAnthropic = createAnthropic({ apiKey, baseURL });
|
|
510
|
-
const ocCompat = createOpenAICompatible({
|
|
511
|
-
name: entry?.label ?? 'opencode',
|
|
512
|
-
baseURL,
|
|
513
|
-
headers,
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
const id = resolvedModelId.toLowerCase();
|
|
517
|
-
if (id.includes('claude')) return ocAnthropic(resolvedModelId);
|
|
518
|
-
if (
|
|
519
|
-
id.includes('qwen3-coder') ||
|
|
520
|
-
id.includes('grok-code') ||
|
|
521
|
-
id.includes('kimi-k2')
|
|
522
|
-
)
|
|
523
|
-
return ocCompat(resolvedModelId);
|
|
524
|
-
return ocOpenAI(resolvedModelId);
|
|
525
|
-
}
|
|
526
|
-
if (provider === 'solforge') {
|
|
527
|
-
const privateKey = process.env.SOLFORGE_PRIVATE_KEY ?? '';
|
|
528
|
-
if (!privateKey) {
|
|
529
|
-
throw new Error(
|
|
530
|
-
'Solforge provider requires SOLFORGE_PRIVATE_KEY (base58 Solana secret).',
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
const baseURL = process.env.SOLFORGE_BASE_URL;
|
|
534
|
-
const rpcURL = process.env.SOLFORGE_SOLANA_RPC_URL;
|
|
535
|
-
const topupAmount = process.env.SOLFORGE_TOPUP_MICRO_USDC;
|
|
536
|
-
return createSolforgeModel(
|
|
537
|
-
model,
|
|
538
|
-
{ privateKey },
|
|
539
|
-
{
|
|
540
|
-
baseURL,
|
|
541
|
-
rpcURL,
|
|
542
|
-
topupAmountMicroUsdc: topupAmount,
|
|
543
|
-
},
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
if (provider === 'zai') {
|
|
547
|
-
return getZaiInstance(cfg, model);
|
|
548
|
-
}
|
|
549
|
-
if (provider === 'zai-coding') {
|
|
550
|
-
return getZaiCodingInstance(cfg, model);
|
|
551
|
-
}
|
|
552
|
-
throw new Error(`Unsupported provider: ${provider}`);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function normalizeModelIdentifier(provider: ProviderId, model: string): string {
|
|
556
|
-
const prefix = `${provider}/`;
|
|
557
|
-
return model.startsWith(prefix) ? model.slice(prefix.length) : model;
|
|
558
|
-
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AGIConfig } from '@agi-cli/sdk';
|
|
2
|
+
import { getAuth } from '@agi-cli/sdk';
|
|
3
|
+
import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
4
|
+
|
|
5
|
+
export async function resolveGoogleModel(model: string, cfg: AGIConfig) {
|
|
6
|
+
const auth = await getAuth('google', cfg.projectRoot);
|
|
7
|
+
if (auth?.type === 'api' && auth.key) {
|
|
8
|
+
const instance = createGoogleGenerativeAI({ apiKey: auth.key });
|
|
9
|
+
return instance(model);
|
|
10
|
+
}
|
|
11
|
+
return google(model);
|
|
12
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AGIConfig, ProviderId } from '@agi-cli/sdk';
|
|
2
|
+
import { getAnthropicInstance } from './anthropic.ts';
|
|
3
|
+
import { resolveOpenAIModel } from './openai.ts';
|
|
4
|
+
import { resolveGoogleModel } from './google.ts';
|
|
5
|
+
import { resolveOpenRouterModel } from './openrouter.ts';
|
|
6
|
+
import { resolveSolforgeModel } from './solforge.ts';
|
|
7
|
+
import { getZaiInstance, getZaiCodingInstance } from './zai.ts';
|
|
8
|
+
import { resolveOpencodeModel } from './opencode.ts';
|
|
9
|
+
|
|
10
|
+
export type ProviderName = ProviderId;
|
|
11
|
+
|
|
12
|
+
export async function resolveModel(
|
|
13
|
+
provider: ProviderName,
|
|
14
|
+
model: string,
|
|
15
|
+
cfg: AGIConfig,
|
|
16
|
+
options?: { systemPrompt?: string },
|
|
17
|
+
) {
|
|
18
|
+
if (provider === 'openai') {
|
|
19
|
+
return resolveOpenAIModel(model, cfg, options);
|
|
20
|
+
}
|
|
21
|
+
if (provider === 'anthropic') {
|
|
22
|
+
const instance = await getAnthropicInstance(cfg);
|
|
23
|
+
return instance(model);
|
|
24
|
+
}
|
|
25
|
+
if (provider === 'google') {
|
|
26
|
+
return resolveGoogleModel(model, cfg);
|
|
27
|
+
}
|
|
28
|
+
if (provider === 'openrouter') {
|
|
29
|
+
return resolveOpenRouterModel(model);
|
|
30
|
+
}
|
|
31
|
+
if (provider === 'opencode') {
|
|
32
|
+
return resolveOpencodeModel(model, cfg);
|
|
33
|
+
}
|
|
34
|
+
if (provider === 'solforge') {
|
|
35
|
+
return resolveSolforgeModel(model);
|
|
36
|
+
}
|
|
37
|
+
if (provider === 'zai') {
|
|
38
|
+
return getZaiInstance(cfg, model);
|
|
39
|
+
}
|
|
40
|
+
if (provider === 'zai-coding') {
|
|
41
|
+
return getZaiCodingInstance(cfg, model);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
44
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AGIConfig } from '@agi-cli/sdk';
|
|
2
|
+
import { getAuth, createOpenAIOAuthModel } from '@agi-cli/sdk';
|
|
3
|
+
import { openai, createOpenAI } from '@ai-sdk/openai';
|
|
4
|
+
|
|
5
|
+
export async function resolveOpenAIModel(
|
|
6
|
+
model: string,
|
|
7
|
+
cfg: AGIConfig,
|
|
8
|
+
options?: { systemPrompt?: string },
|
|
9
|
+
) {
|
|
10
|
+
const auth = await getAuth('openai', cfg.projectRoot);
|
|
11
|
+
if (auth?.type === 'oauth') {
|
|
12
|
+
const isCodexModel = model.toLowerCase().includes('codex');
|
|
13
|
+
return createOpenAIOAuthModel(model, {
|
|
14
|
+
oauth: auth,
|
|
15
|
+
projectRoot: cfg.projectRoot,
|
|
16
|
+
reasoningEffort: isCodexModel ? 'high' : 'medium',
|
|
17
|
+
reasoningSummary: 'auto',
|
|
18
|
+
instructions: options?.systemPrompt,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (auth?.type === 'api' && auth.key) {
|
|
22
|
+
const instance = createOpenAI({ apiKey: auth.key });
|
|
23
|
+
return instance(model);
|
|
24
|
+
}
|
|
25
|
+
return openai(model);
|
|
26
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AGIConfig, ProviderId } from '@agi-cli/sdk';
|
|
2
|
+
import { catalog } from '@agi-cli/sdk';
|
|
3
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
4
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
5
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
6
|
+
|
|
7
|
+
function normalizeModelIdentifier(provider: ProviderId, model: string): string {
|
|
8
|
+
const prefix = `${provider}/`;
|
|
9
|
+
return model.startsWith(prefix) ? model.slice(prefix.length) : model;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveOpencodeModel(model: string, _cfg: AGIConfig) {
|
|
13
|
+
const entry = catalog.opencode;
|
|
14
|
+
const normalizedModel = normalizeModelIdentifier('opencode', model);
|
|
15
|
+
const modelInfo =
|
|
16
|
+
entry?.models.find((m) => m.id === normalizedModel) ??
|
|
17
|
+
entry?.models.find((m) => m.id === model);
|
|
18
|
+
const resolvedModelId = modelInfo?.id ?? normalizedModel ?? model;
|
|
19
|
+
const binding = modelInfo?.provider?.npm ?? entry?.npm;
|
|
20
|
+
const apiKey = process.env.OPENCODE_API_KEY ?? '';
|
|
21
|
+
const baseURL =
|
|
22
|
+
modelInfo?.provider?.baseURL ||
|
|
23
|
+
modelInfo?.provider?.api ||
|
|
24
|
+
entry?.api ||
|
|
25
|
+
'https://opencode.ai/zen/v1';
|
|
26
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
27
|
+
if (binding === '@ai-sdk/openai') {
|
|
28
|
+
const instance = createOpenAI({ apiKey, baseURL });
|
|
29
|
+
return instance(resolvedModelId);
|
|
30
|
+
}
|
|
31
|
+
if (binding === '@ai-sdk/anthropic') {
|
|
32
|
+
const instance = createAnthropic({ apiKey, baseURL });
|
|
33
|
+
return instance(resolvedModelId);
|
|
34
|
+
}
|
|
35
|
+
if (binding === '@ai-sdk/openai-compatible') {
|
|
36
|
+
const instance = createOpenAICompatible({
|
|
37
|
+
name: entry?.label ?? 'opencode',
|
|
38
|
+
baseURL,
|
|
39
|
+
headers,
|
|
40
|
+
});
|
|
41
|
+
return instance(resolvedModelId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ocOpenAI = createOpenAI({ apiKey, baseURL });
|
|
45
|
+
const ocAnthropic = createAnthropic({ apiKey, baseURL });
|
|
46
|
+
const ocCompat = createOpenAICompatible({
|
|
47
|
+
name: entry?.label ?? 'opencode',
|
|
48
|
+
baseURL,
|
|
49
|
+
headers,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const id = resolvedModelId.toLowerCase();
|
|
53
|
+
if (id.includes('claude')) return ocAnthropic(resolvedModelId);
|
|
54
|
+
if (
|
|
55
|
+
id.includes('qwen3-coder') ||
|
|
56
|
+
id.includes('grok-code') ||
|
|
57
|
+
id.includes('kimi-k2')
|
|
58
|
+
)
|
|
59
|
+
return ocCompat(resolvedModelId);
|
|
60
|
+
return ocOpenAI(resolvedModelId);
|
|
61
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
2
|
+
|
|
3
|
+
export function getOpenRouterInstance() {
|
|
4
|
+
const apiKey = process.env.OPENROUTER_API_KEY ?? '';
|
|
5
|
+
return createOpenRouter({ apiKey });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function resolveOpenRouterModel(model: string) {
|
|
9
|
+
const openrouter = getOpenRouterInstance();
|
|
10
|
+
return openrouter.chat(model);
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createSolforgeModel } from '@agi-cli/sdk';
|
|
2
|
+
|
|
3
|
+
export function resolveSolforgeModel(model: string) {
|
|
4
|
+
const privateKey = process.env.SOLFORGE_PRIVATE_KEY ?? '';
|
|
5
|
+
if (!privateKey) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
'Solforge provider requires SOLFORGE_PRIVATE_KEY (base58 Solana secret).',
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
const baseURL = process.env.SOLFORGE_BASE_URL;
|
|
11
|
+
const rpcURL = process.env.SOLFORGE_SOLANA_RPC_URL;
|
|
12
|
+
const topupAmount = process.env.SOLFORGE_TOPUP_MICRO_USDC;
|
|
13
|
+
return createSolforgeModel(
|
|
14
|
+
model,
|
|
15
|
+
{ privateKey },
|
|
16
|
+
{
|
|
17
|
+
baseURL,
|
|
18
|
+
rpcURL,
|
|
19
|
+
topupAmountMicroUsdc: topupAmount,
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AGIConfig } from '@agi-cli/sdk';
|
|
2
|
+
import { catalog, getAuth } from '@agi-cli/sdk';
|
|
3
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
4
|
+
|
|
5
|
+
export async function getZaiInstance(cfg: AGIConfig, model: string) {
|
|
6
|
+
const auth = await getAuth('zai', cfg.projectRoot);
|
|
7
|
+
const entry = catalog.zai;
|
|
8
|
+
|
|
9
|
+
let apiKey = '';
|
|
10
|
+
const baseURL = entry?.api || 'https://api.z.ai/api/paas/v4';
|
|
11
|
+
|
|
12
|
+
if (auth?.type === 'api' && auth.key) {
|
|
13
|
+
apiKey = auth.key;
|
|
14
|
+
} else {
|
|
15
|
+
apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
19
|
+
|
|
20
|
+
const instance = createOpenAICompatible({
|
|
21
|
+
name: entry?.label ?? 'Z.AI',
|
|
22
|
+
baseURL,
|
|
23
|
+
headers,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return instance(model);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function getZaiCodingInstance(cfg: AGIConfig, model: string) {
|
|
30
|
+
const auth =
|
|
31
|
+
(await getAuth('zai', cfg.projectRoot)) ||
|
|
32
|
+
(await getAuth('zai-coding', cfg.projectRoot));
|
|
33
|
+
const entry = catalog['zai-coding'];
|
|
34
|
+
|
|
35
|
+
let apiKey = '';
|
|
36
|
+
const baseURL = entry?.api || 'https://api.z.ai/api/coding/paas/v4';
|
|
37
|
+
|
|
38
|
+
if (auth?.type === 'api' && auth.key) {
|
|
39
|
+
apiKey = auth.key;
|
|
40
|
+
} else {
|
|
41
|
+
apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
45
|
+
|
|
46
|
+
const instance = createOpenAICompatible({
|
|
47
|
+
name: entry?.label ?? 'Z.AI Coding',
|
|
48
|
+
baseURL,
|
|
49
|
+
headers,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return instance(model);
|
|
53
|
+
}
|