@agi-cli/sdk 0.1.133 → 0.1.135
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/README.md +35 -71
- package/package.json +1 -1
- package/src/core/src/terminals/manager.ts +1 -1
- package/src/index.ts +23 -0
- package/src/providers/src/anthropic-caching.ts +202 -0
- package/src/providers/src/anthropic-oauth-client.ts +157 -0
- package/src/providers/src/google-client.ts +16 -0
- package/src/providers/src/index.ts +23 -0
- package/src/providers/src/openai-oauth-client.ts +0 -15
- package/src/providers/src/opencode-client.ts +80 -0
- package/src/providers/src/openrouter-client.ts +30 -0
- package/src/providers/src/solforge-client.ts +17 -82
- package/src/providers/src/utils.ts +21 -0
- package/src/providers/src/zai-client.ts +47 -0
package/README.md
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
`@agi-cli/sdk` is the unified SDK for building AI agents with AGI CLI.
|
|
7
|
+
`@agi-cli/sdk` is the unified SDK for building AI agents with AGI CLI. All authentication, configuration, providers, prompts, tools, and core AI functionality are included in this single package.
|
|
8
8
|
|
|
9
9
|
**Why use the SDK?**
|
|
10
10
|
- ✅ **Single import**: All functionality from one package
|
|
11
11
|
- ✅ **Tree-shakable**: Bundlers only include what you use
|
|
12
12
|
- ✅ **Type-safe**: Full TypeScript support with comprehensive types
|
|
13
13
|
- ✅ **Zero circular dependencies**: Clean architecture
|
|
14
|
-
- ✅ **Consistent API**: No need to remember which
|
|
14
|
+
- ✅ **Consistent API**: No need to remember which module exports what
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@ bun add @agi-cli/sdk
|
|
|
25
25
|
import { generateText, resolveModel } from '@agi-cli/sdk';
|
|
26
26
|
import type { ProviderId } from '@agi-cli/sdk';
|
|
27
27
|
|
|
28
|
-
const model = resolveModel('anthropic', 'claude-
|
|
28
|
+
const model = resolveModel('anthropic', 'claude-sonnet-4-20250514');
|
|
29
29
|
|
|
30
30
|
const { text } = await generateText({
|
|
31
31
|
model,
|
|
@@ -37,7 +37,7 @@ console.log(text);
|
|
|
37
37
|
|
|
38
38
|
## What's Included?
|
|
39
39
|
|
|
40
|
-
### Types
|
|
40
|
+
### Types
|
|
41
41
|
|
|
42
42
|
All shared types are available:
|
|
43
43
|
|
|
@@ -52,7 +52,7 @@ import type {
|
|
|
52
52
|
} from '@agi-cli/sdk';
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
### Providers
|
|
55
|
+
### Providers
|
|
56
56
|
|
|
57
57
|
Provider catalog and utilities:
|
|
58
58
|
|
|
@@ -72,16 +72,16 @@ import {
|
|
|
72
72
|
} from '@agi-cli/sdk';
|
|
73
73
|
|
|
74
74
|
// Check available providers
|
|
75
|
-
console.log(providerIds); // ['openai', 'anthropic', 'google', 'openrouter', 'opencode']
|
|
75
|
+
console.log(providerIds); // ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'solforge']
|
|
76
76
|
|
|
77
77
|
// Get model information
|
|
78
78
|
const models = catalog.anthropic.models;
|
|
79
79
|
|
|
80
80
|
// Validate provider/model combination
|
|
81
|
-
const result = validateProviderModel('anthropic', 'claude-
|
|
81
|
+
const result = validateProviderModel('anthropic', 'claude-sonnet-4-20250514');
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
### Authentication
|
|
84
|
+
### Authentication
|
|
85
85
|
|
|
86
86
|
Manage API keys and OAuth:
|
|
87
87
|
|
|
@@ -106,35 +106,30 @@ const url = await authorize('anthropic');
|
|
|
106
106
|
console.log(`Visit: ${url}`);
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
### Configuration
|
|
109
|
+
### Configuration
|
|
110
110
|
|
|
111
111
|
Load and manage configuration:
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
|
-
import { loadConfig
|
|
114
|
+
import { loadConfig } from '@agi-cli/sdk';
|
|
115
115
|
import type { AGIConfig } from '@agi-cli/sdk';
|
|
116
116
|
|
|
117
117
|
const config = await loadConfig();
|
|
118
118
|
console.log(config.provider); // 'anthropic'
|
|
119
|
-
console.log(config.model); // 'claude-
|
|
119
|
+
console.log(config.model); // 'claude-sonnet-4-20250514'
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
### Prompts
|
|
122
|
+
### Prompts
|
|
123
123
|
|
|
124
124
|
Pre-built system prompts:
|
|
125
125
|
|
|
126
126
|
```typescript
|
|
127
|
-
import {
|
|
127
|
+
import { providerBasePrompt } from '@agi-cli/sdk';
|
|
128
128
|
|
|
129
|
-
const prompt =
|
|
130
|
-
workdir: '/home/user/project',
|
|
131
|
-
platform: 'linux',
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const context = codeContext({ includeGitInfo: true });
|
|
129
|
+
const prompt = providerBasePrompt('anthropic');
|
|
135
130
|
```
|
|
136
131
|
|
|
137
|
-
### Core AI Functions
|
|
132
|
+
### Core AI Functions
|
|
138
133
|
|
|
139
134
|
AI SDK re-exports and utilities:
|
|
140
135
|
|
|
@@ -181,28 +176,7 @@ const { object } = await generateObject({
|
|
|
181
176
|
const tools = await discoverProjectTools('/path/to/project');
|
|
182
177
|
```
|
|
183
178
|
|
|
184
|
-
###
|
|
185
|
-
|
|
186
|
-
Database access:
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { getDb, dbSchema } from '@agi-cli/sdk';
|
|
190
|
-
|
|
191
|
-
const db = getDb();
|
|
192
|
-
const messages = await db.select().from(dbSchema.messages);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Server (from `@agi-cli/server`)
|
|
196
|
-
|
|
197
|
-
Create an HTTP server:
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
import { createServer } from '@agi-cli/sdk';
|
|
201
|
-
|
|
202
|
-
const app = createServer();
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Error Handling (from `@agi-cli/core`)
|
|
179
|
+
### Error Handling
|
|
206
180
|
|
|
207
181
|
Typed error classes:
|
|
208
182
|
|
|
@@ -237,39 +211,30 @@ The SDK is fully tree-shakable. Modern bundlers (Vite, Rollup, esbuild, webpack)
|
|
|
237
211
|
import { generateText, resolveModel } from '@agi-cli/sdk';
|
|
238
212
|
```
|
|
239
213
|
|
|
240
|
-
## Direct Package Access (Not Recommended)
|
|
241
|
-
|
|
242
|
-
While you _can_ import directly from individual packages, we recommend using the SDK for consistency:
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
// ❌ Discouraged - fragmented imports
|
|
246
|
-
import { catalog } from '@agi-cli/providers';
|
|
247
|
-
import { generateText } from '@agi-cli/core';
|
|
248
|
-
import type { ProviderId } from '@agi-cli/types';
|
|
249
|
-
|
|
250
|
-
// ✅ Recommended - single source of truth
|
|
251
|
-
import { catalog, generateText } from '@agi-cli/sdk';
|
|
252
|
-
import type { ProviderId } from '@agi-cli/sdk';
|
|
253
|
-
```
|
|
254
|
-
|
|
255
214
|
## Architecture
|
|
256
215
|
|
|
257
|
-
The SDK
|
|
216
|
+
The SDK contains all functionality internally:
|
|
258
217
|
|
|
259
218
|
```
|
|
260
|
-
@agi-cli/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
219
|
+
@agi-cli/sdk/src/
|
|
220
|
+
├── auth/ ← Authentication (OAuth, API keys)
|
|
221
|
+
├── config/ ← Configuration (global + project)
|
|
222
|
+
├── core/ ← Core AI functionality
|
|
223
|
+
│ ├── providers/ (model resolution)
|
|
224
|
+
│ ├── tools/ (builtin tools)
|
|
225
|
+
│ ├── streaming/ (artifacts)
|
|
226
|
+
│ └── errors.ts (error classes)
|
|
227
|
+
├── prompts/ ← System prompts
|
|
228
|
+
├── providers/ ← Provider catalog & utilities
|
|
229
|
+
└── index.ts ← Main exports
|
|
271
230
|
```
|
|
272
231
|
|
|
232
|
+
Related packages:
|
|
233
|
+
- `@agi-cli/database` - SQLite persistence (depends on sdk)
|
|
234
|
+
- `@agi-cli/server` - HTTP API (depends on sdk, database)
|
|
235
|
+
- `@agi-cli/api` - Type-safe API client (standalone)
|
|
236
|
+
- `@agi-cli/web-sdk` - React hooks & components (depends on api)
|
|
237
|
+
|
|
273
238
|
## Examples
|
|
274
239
|
|
|
275
240
|
### Basic Agent
|
|
@@ -356,8 +321,7 @@ import type {
|
|
|
356
321
|
CoreMessage,
|
|
357
322
|
Tool,
|
|
358
323
|
DiscoveredTool,
|
|
359
|
-
Artifact
|
|
360
|
-
ExecutionContext
|
|
324
|
+
Artifact
|
|
361
325
|
} from '@agi-cli/sdk';
|
|
362
326
|
|
|
363
327
|
// All types are fully documented and type-safe
|
package/package.json
CHANGED
|
@@ -112,7 +112,7 @@ export class TerminalManager {
|
|
|
112
112
|
async killAll(): Promise<void> {
|
|
113
113
|
const killPromises = Array.from(this.terminals.keys()).map((id) =>
|
|
114
114
|
this.kill(id).catch((err) =>
|
|
115
|
-
|
|
115
|
+
logger.error(`Failed to kill terminal ${id}`, err),
|
|
116
116
|
),
|
|
117
117
|
);
|
|
118
118
|
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,8 @@ export {
|
|
|
42
42
|
hasModel,
|
|
43
43
|
getFastModel,
|
|
44
44
|
getFastModelForAuth,
|
|
45
|
+
getModelNpmBinding,
|
|
46
|
+
isAnthropicBasedModel,
|
|
45
47
|
} from './providers/src/index.ts';
|
|
46
48
|
export {
|
|
47
49
|
isProviderAuthorized,
|
|
@@ -77,6 +79,27 @@ export {
|
|
|
77
79
|
filterModelsForAuthType,
|
|
78
80
|
getOAuthModelPrefixes,
|
|
79
81
|
} from './providers/src/index.ts';
|
|
82
|
+
export {
|
|
83
|
+
addAnthropicCacheControl,
|
|
84
|
+
createAnthropicCachingFetch,
|
|
85
|
+
createConditionalCachingFetch,
|
|
86
|
+
} from './providers/src/index.ts';
|
|
87
|
+
export {
|
|
88
|
+
createAnthropicOAuthFetch,
|
|
89
|
+
createAnthropicOAuthModel,
|
|
90
|
+
} from './providers/src/index.ts';
|
|
91
|
+
export type { AnthropicOAuthConfig } from './providers/src/index.ts';
|
|
92
|
+
export { createGoogleModel } from './providers/src/index.ts';
|
|
93
|
+
export type { GoogleProviderConfig } from './providers/src/index.ts';
|
|
94
|
+
export { createZaiModel, createZaiCodingModel } from './providers/src/index.ts';
|
|
95
|
+
export type { ZaiProviderConfig } from './providers/src/index.ts';
|
|
96
|
+
export {
|
|
97
|
+
getOpenRouterInstance,
|
|
98
|
+
createOpenRouterModel,
|
|
99
|
+
} from './providers/src/index.ts';
|
|
100
|
+
export type { OpenRouterProviderConfig } from './providers/src/index.ts';
|
|
101
|
+
export { createOpencodeModel } from './providers/src/index.ts';
|
|
102
|
+
export type { OpencodeProviderConfig } from './providers/src/index.ts';
|
|
80
103
|
|
|
81
104
|
// =======================
|
|
82
105
|
// Authentication (from internal auth module)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
type MessageBlock = {
|
|
2
|
+
type: string;
|
|
3
|
+
cache_control?: { type: string };
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type Message = {
|
|
8
|
+
role: string;
|
|
9
|
+
content: unknown;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type ParsedBody = {
|
|
14
|
+
system?: MessageBlock[];
|
|
15
|
+
messages?: Message[];
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function addAnthropicCacheControl(parsed: ParsedBody): ParsedBody {
|
|
20
|
+
const MAX_SYSTEM_CACHE = 1;
|
|
21
|
+
const MAX_MESSAGE_CACHE = 1;
|
|
22
|
+
let systemCacheUsed = 0;
|
|
23
|
+
let messageCacheUsed = 0;
|
|
24
|
+
|
|
25
|
+
if (parsed.system && Array.isArray(parsed.system)) {
|
|
26
|
+
parsed.system = parsed.system.map((block, index) => {
|
|
27
|
+
if (block.cache_control) return block;
|
|
28
|
+
if (
|
|
29
|
+
systemCacheUsed < MAX_SYSTEM_CACHE &&
|
|
30
|
+
index === 0 &&
|
|
31
|
+
block.type === 'text'
|
|
32
|
+
) {
|
|
33
|
+
systemCacheUsed++;
|
|
34
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
35
|
+
}
|
|
36
|
+
return block;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
41
|
+
const messageCount = parsed.messages.length;
|
|
42
|
+
parsed.messages = parsed.messages.map((msg, msgIndex) => {
|
|
43
|
+
const isLast = msgIndex === messageCount - 1;
|
|
44
|
+
|
|
45
|
+
if (Array.isArray(msg.content)) {
|
|
46
|
+
const blocks = msg.content as MessageBlock[];
|
|
47
|
+
const content = blocks.map((block, blockIndex) => {
|
|
48
|
+
if (block.cache_control) return block;
|
|
49
|
+
if (
|
|
50
|
+
isLast &&
|
|
51
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
52
|
+
blockIndex === blocks.length - 1
|
|
53
|
+
) {
|
|
54
|
+
messageCacheUsed++;
|
|
55
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
56
|
+
}
|
|
57
|
+
return block;
|
|
58
|
+
});
|
|
59
|
+
return { ...msg, content };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
isLast &&
|
|
64
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
65
|
+
typeof msg.content === 'string'
|
|
66
|
+
) {
|
|
67
|
+
messageCacheUsed++;
|
|
68
|
+
return {
|
|
69
|
+
...msg,
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: msg.content,
|
|
74
|
+
cache_control: { type: 'ephemeral' },
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return msg;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function createAnthropicCachingFetch(): typeof fetch {
|
|
88
|
+
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
89
|
+
let body = init?.body;
|
|
90
|
+
if (body && typeof body === 'string') {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(body);
|
|
93
|
+
const modified = addAnthropicCacheControl(parsed);
|
|
94
|
+
body = JSON.stringify(modified);
|
|
95
|
+
} catch {
|
|
96
|
+
// If parsing fails, send as-is
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return fetch(input, { ...init, body });
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createConditionalCachingFetch(
|
|
104
|
+
shouldCache: (model: string) => boolean,
|
|
105
|
+
model: string,
|
|
106
|
+
): typeof fetch {
|
|
107
|
+
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
108
|
+
if (!shouldCache(model)) {
|
|
109
|
+
return fetch(input, init);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let body = init?.body;
|
|
113
|
+
if (body && typeof body === 'string') {
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(body);
|
|
116
|
+
|
|
117
|
+
const MAX_SYSTEM_CACHE = 1;
|
|
118
|
+
const MAX_MESSAGE_CACHE = 1;
|
|
119
|
+
let systemCacheUsed = 0;
|
|
120
|
+
let messageCacheUsed = 0;
|
|
121
|
+
|
|
122
|
+
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
123
|
+
const messageCount = parsed.messages.length;
|
|
124
|
+
parsed.messages = parsed.messages.map(
|
|
125
|
+
(msg: Message, msgIndex: number) => {
|
|
126
|
+
if (msg.role === 'system' && systemCacheUsed < MAX_SYSTEM_CACHE) {
|
|
127
|
+
systemCacheUsed++;
|
|
128
|
+
if (typeof msg.content === 'string') {
|
|
129
|
+
return {
|
|
130
|
+
...msg,
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: 'text',
|
|
134
|
+
text: msg.content,
|
|
135
|
+
cache_control: { type: 'ephemeral' },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(msg.content)) {
|
|
141
|
+
const blocks = msg.content as MessageBlock[];
|
|
142
|
+
const content = blocks.map((block, i) => {
|
|
143
|
+
if (i === blocks.length - 1 && !block.cache_control) {
|
|
144
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
145
|
+
}
|
|
146
|
+
return block;
|
|
147
|
+
});
|
|
148
|
+
return { ...msg, content };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const isLast = msgIndex === messageCount - 1;
|
|
153
|
+
|
|
154
|
+
if (Array.isArray(msg.content)) {
|
|
155
|
+
const blocks = msg.content as MessageBlock[];
|
|
156
|
+
const content = blocks.map((block, blockIndex) => {
|
|
157
|
+
if (block.cache_control) return block;
|
|
158
|
+
if (
|
|
159
|
+
isLast &&
|
|
160
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
161
|
+
blockIndex === blocks.length - 1
|
|
162
|
+
) {
|
|
163
|
+
messageCacheUsed++;
|
|
164
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
165
|
+
}
|
|
166
|
+
return block;
|
|
167
|
+
});
|
|
168
|
+
return { ...msg, content };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
isLast &&
|
|
173
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
174
|
+
typeof msg.content === 'string'
|
|
175
|
+
) {
|
|
176
|
+
messageCacheUsed++;
|
|
177
|
+
return {
|
|
178
|
+
...msg,
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: 'text',
|
|
182
|
+
text: msg.content,
|
|
183
|
+
cache_control: { type: 'ephemeral' },
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return msg;
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
body = JSON.stringify(parsed);
|
|
195
|
+
} catch {
|
|
196
|
+
// If parsing fails, send as-is
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return fetch(input, { ...init, body });
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
2
|
+
import { addAnthropicCacheControl } from './anthropic-caching.ts';
|
|
3
|
+
|
|
4
|
+
const CLAUDE_CLI_VERSION = '1.0.61';
|
|
5
|
+
|
|
6
|
+
export type AnthropicOAuthConfig = {
|
|
7
|
+
oauth: {
|
|
8
|
+
access: string;
|
|
9
|
+
refresh: string;
|
|
10
|
+
expires: number;
|
|
11
|
+
};
|
|
12
|
+
toolNameTransformer?: (name: string) => string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function buildOAuthHeaders(accessToken: string): Record<string, string> {
|
|
16
|
+
const headers: Record<string, string> = {
|
|
17
|
+
authorization: `Bearer ${accessToken}`,
|
|
18
|
+
'anthropic-beta':
|
|
19
|
+
'claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14',
|
|
20
|
+
'anthropic-dangerous-direct-browser-access': 'true',
|
|
21
|
+
'anthropic-version': '2023-06-01',
|
|
22
|
+
'user-agent': `claude-cli/${CLAUDE_CLI_VERSION} (external, cli)`,
|
|
23
|
+
'x-app': 'cli',
|
|
24
|
+
'content-type': 'application/json',
|
|
25
|
+
accept: 'application/json',
|
|
26
|
+
'x-stainless-arch': process.arch === 'arm64' ? 'arm64' : 'x64',
|
|
27
|
+
'x-stainless-helper-method': 'stream',
|
|
28
|
+
'x-stainless-lang': 'js',
|
|
29
|
+
'x-stainless-os':
|
|
30
|
+
process.platform === 'darwin'
|
|
31
|
+
? 'MacOS'
|
|
32
|
+
: process.platform === 'win32'
|
|
33
|
+
? 'Windows'
|
|
34
|
+
: 'Linux',
|
|
35
|
+
'x-stainless-package-version': '0.70.0',
|
|
36
|
+
'x-stainless-retry-count': '0',
|
|
37
|
+
'x-stainless-runtime': 'node',
|
|
38
|
+
'x-stainless-runtime-version': process.version,
|
|
39
|
+
'x-stainless-timeout': '600',
|
|
40
|
+
};
|
|
41
|
+
return headers;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function filterExistingHeaders(
|
|
45
|
+
initHeaders: HeadersInit | undefined,
|
|
46
|
+
): Record<string, string> {
|
|
47
|
+
const headers: Record<string, string> = {};
|
|
48
|
+
if (!initHeaders) return headers;
|
|
49
|
+
|
|
50
|
+
if (initHeaders instanceof Headers) {
|
|
51
|
+
initHeaders.forEach((value, key) => {
|
|
52
|
+
if (key.toLowerCase() !== 'x-api-key') {
|
|
53
|
+
headers[key] = value;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
} else if (Array.isArray(initHeaders)) {
|
|
57
|
+
for (const [key, value] of initHeaders) {
|
|
58
|
+
if (
|
|
59
|
+
key &&
|
|
60
|
+
key.toLowerCase() !== 'x-api-key' &&
|
|
61
|
+
typeof value === 'string'
|
|
62
|
+
) {
|
|
63
|
+
headers[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
for (const [key, value] of Object.entries(initHeaders)) {
|
|
68
|
+
if (key.toLowerCase() !== 'x-api-key' && typeof value === 'string') {
|
|
69
|
+
headers[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return headers;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createAnthropicOAuthFetch(
|
|
77
|
+
config: AnthropicOAuthConfig,
|
|
78
|
+
): typeof fetch {
|
|
79
|
+
const { oauth, toolNameTransformer } = config;
|
|
80
|
+
|
|
81
|
+
return async (input: string | URL | Request, init?: RequestInit) => {
|
|
82
|
+
const existingHeaders = filterExistingHeaders(init?.headers);
|
|
83
|
+
const oauthHeaders = buildOAuthHeaders(oauth.access);
|
|
84
|
+
const headers = { ...existingHeaders, ...oauthHeaders };
|
|
85
|
+
|
|
86
|
+
let url = typeof input === 'string' ? input : input.toString();
|
|
87
|
+
if (url.includes('/v1/messages') && !url.includes('beta=true')) {
|
|
88
|
+
url += url.includes('?') ? '&beta=true' : '?beta=true';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let body = init?.body;
|
|
92
|
+
if (body && typeof body === 'string') {
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(body);
|
|
95
|
+
|
|
96
|
+
if (toolNameTransformer) {
|
|
97
|
+
if (parsed.tools && Array.isArray(parsed.tools)) {
|
|
98
|
+
parsed.tools = parsed.tools.map(
|
|
99
|
+
(tool: { name: string; [key: string]: unknown }) => ({
|
|
100
|
+
...tool,
|
|
101
|
+
name: toolNameTransformer(tool.name),
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
107
|
+
parsed.messages = parsed.messages.map(
|
|
108
|
+
(msg: { content: unknown; [key: string]: unknown }) => {
|
|
109
|
+
if (Array.isArray(msg.content)) {
|
|
110
|
+
const content = msg.content.map(
|
|
111
|
+
(block: {
|
|
112
|
+
type: string;
|
|
113
|
+
name?: string;
|
|
114
|
+
[key: string]: unknown;
|
|
115
|
+
}) => {
|
|
116
|
+
if (
|
|
117
|
+
(block.type === 'tool_use' ||
|
|
118
|
+
block.type === 'tool_result') &&
|
|
119
|
+
block.name
|
|
120
|
+
) {
|
|
121
|
+
return {
|
|
122
|
+
...block,
|
|
123
|
+
name: toolNameTransformer(block.name),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return block;
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
return { ...msg, content };
|
|
130
|
+
}
|
|
131
|
+
return msg;
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const withCache = addAnthropicCacheControl(parsed);
|
|
138
|
+
body = JSON.stringify(withCache);
|
|
139
|
+
} catch {
|
|
140
|
+
// If parsing fails, send as-is
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return fetch(url, { ...init, body, headers });
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function createAnthropicOAuthModel(
|
|
149
|
+
model: string,
|
|
150
|
+
config: AnthropicOAuthConfig,
|
|
151
|
+
) {
|
|
152
|
+
const customFetch = createAnthropicOAuthFetch(config);
|
|
153
|
+
return createAnthropic({
|
|
154
|
+
apiKey: '',
|
|
155
|
+
fetch: customFetch as typeof fetch,
|
|
156
|
+
})(model);
|
|
157
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
2
|
+
|
|
3
|
+
export type GoogleProviderConfig = {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function createGoogleModel(
|
|
8
|
+
model: string,
|
|
9
|
+
config?: GoogleProviderConfig,
|
|
10
|
+
) {
|
|
11
|
+
if (config?.apiKey) {
|
|
12
|
+
const instance = createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
13
|
+
return instance(model);
|
|
14
|
+
}
|
|
15
|
+
return google(model);
|
|
16
|
+
}
|
|
@@ -13,6 +13,8 @@ export {
|
|
|
13
13
|
hasModel,
|
|
14
14
|
getFastModel,
|
|
15
15
|
getFastModelForAuth,
|
|
16
|
+
getModelNpmBinding,
|
|
17
|
+
isAnthropicBasedModel,
|
|
16
18
|
} from './utils.ts';
|
|
17
19
|
export { validateProviderModel } from './validate.ts';
|
|
18
20
|
export { estimateModelCostUsd } from './pricing.ts';
|
|
@@ -41,3 +43,24 @@ export {
|
|
|
41
43
|
filterModelsForAuthType,
|
|
42
44
|
getOAuthModelPrefixes,
|
|
43
45
|
} from './oauth-models.ts';
|
|
46
|
+
export {
|
|
47
|
+
addAnthropicCacheControl,
|
|
48
|
+
createAnthropicCachingFetch,
|
|
49
|
+
createConditionalCachingFetch,
|
|
50
|
+
} from './anthropic-caching.ts';
|
|
51
|
+
export {
|
|
52
|
+
createAnthropicOAuthFetch,
|
|
53
|
+
createAnthropicOAuthModel,
|
|
54
|
+
} from './anthropic-oauth-client.ts';
|
|
55
|
+
export type { AnthropicOAuthConfig } from './anthropic-oauth-client.ts';
|
|
56
|
+
export { createGoogleModel } from './google-client.ts';
|
|
57
|
+
export type { GoogleProviderConfig } from './google-client.ts';
|
|
58
|
+
export { createZaiModel, createZaiCodingModel } from './zai-client.ts';
|
|
59
|
+
export type { ZaiProviderConfig } from './zai-client.ts';
|
|
60
|
+
export {
|
|
61
|
+
getOpenRouterInstance,
|
|
62
|
+
createOpenRouterModel,
|
|
63
|
+
} from './openrouter-client.ts';
|
|
64
|
+
export type { OpenRouterProviderConfig } from './openrouter-client.ts';
|
|
65
|
+
export { createOpencodeModel } from './opencode-client.ts';
|
|
66
|
+
export type { OpencodeProviderConfig } from './opencode-client.ts';
|
|
@@ -14,8 +14,6 @@ export type OpenAIOAuthConfig = {
|
|
|
14
14
|
instructions?: string;
|
|
15
15
|
reasoningEffort?: 'none' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
16
16
|
reasoningSummary?: 'auto' | 'detailed';
|
|
17
|
-
promptCacheKey?: string;
|
|
18
|
-
promptCacheRetention?: 'in_memory' | '24h';
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
async function ensureValidToken(
|
|
@@ -185,19 +183,6 @@ export function createOpenAIOAuthFetch(config: OpenAIOAuthConfig) {
|
|
|
185
183
|
}
|
|
186
184
|
}
|
|
187
185
|
|
|
188
|
-
const cacheKey =
|
|
189
|
-
parsed.providerOptions?.openai?.promptCacheKey ||
|
|
190
|
-
config.promptCacheKey;
|
|
191
|
-
const cacheRetention =
|
|
192
|
-
parsed.providerOptions?.openai?.promptCacheRetention ||
|
|
193
|
-
config.promptCacheRetention;
|
|
194
|
-
if (cacheKey) {
|
|
195
|
-
parsed.prompt_cache_key = cacheKey;
|
|
196
|
-
}
|
|
197
|
-
if (cacheRetention) {
|
|
198
|
-
parsed.prompt_cache_retention = cacheRetention;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
186
|
delete parsed.max_output_tokens;
|
|
202
187
|
delete parsed.max_completion_tokens;
|
|
203
188
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
2
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
3
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
4
|
+
import { catalog } from './catalog-merged.ts';
|
|
5
|
+
import { createAnthropicCachingFetch } from './anthropic-caching.ts';
|
|
6
|
+
import type { ProviderId } from '../../types/src/index.ts';
|
|
7
|
+
|
|
8
|
+
export type OpencodeProviderConfig = {
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function normalizeModelIdentifier(provider: ProviderId, model: string): string {
|
|
13
|
+
const prefix = `${provider}/`;
|
|
14
|
+
return model.startsWith(prefix) ? model.slice(prefix.length) : model;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createOpencodeModel(
|
|
18
|
+
model: string,
|
|
19
|
+
config?: OpencodeProviderConfig,
|
|
20
|
+
) {
|
|
21
|
+
const entry = catalog.opencode;
|
|
22
|
+
const normalizedModel = normalizeModelIdentifier('opencode', model);
|
|
23
|
+
const modelInfo =
|
|
24
|
+
entry?.models.find((m) => m.id === normalizedModel) ??
|
|
25
|
+
entry?.models.find((m) => m.id === model);
|
|
26
|
+
const resolvedModelId = modelInfo?.id ?? normalizedModel ?? model;
|
|
27
|
+
const binding = modelInfo?.provider?.npm ?? entry?.npm;
|
|
28
|
+
const apiKey = config?.apiKey ?? process.env.OPENCODE_API_KEY ?? '';
|
|
29
|
+
const baseURL =
|
|
30
|
+
modelInfo?.provider?.baseURL ||
|
|
31
|
+
modelInfo?.provider?.api ||
|
|
32
|
+
entry?.api ||
|
|
33
|
+
'https://opencode.ai/zen/v1';
|
|
34
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
35
|
+
|
|
36
|
+
if (binding === '@ai-sdk/openai') {
|
|
37
|
+
const instance = createOpenAI({ apiKey, baseURL });
|
|
38
|
+
return instance(resolvedModelId);
|
|
39
|
+
}
|
|
40
|
+
if (binding === '@ai-sdk/anthropic') {
|
|
41
|
+
const cachingFetch = createAnthropicCachingFetch();
|
|
42
|
+
const instance = createAnthropic({
|
|
43
|
+
apiKey,
|
|
44
|
+
baseURL,
|
|
45
|
+
fetch: cachingFetch as typeof fetch,
|
|
46
|
+
});
|
|
47
|
+
return instance(resolvedModelId);
|
|
48
|
+
}
|
|
49
|
+
if (binding === '@ai-sdk/openai-compatible') {
|
|
50
|
+
const instance = createOpenAICompatible({
|
|
51
|
+
name: entry?.label ?? 'opencode',
|
|
52
|
+
baseURL,
|
|
53
|
+
headers,
|
|
54
|
+
});
|
|
55
|
+
return instance(resolvedModelId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const ocOpenAI = createOpenAI({ apiKey, baseURL });
|
|
59
|
+
const cachingFetch = createAnthropicCachingFetch();
|
|
60
|
+
const ocAnthropic = createAnthropic({
|
|
61
|
+
apiKey,
|
|
62
|
+
baseURL,
|
|
63
|
+
fetch: cachingFetch as typeof fetch,
|
|
64
|
+
});
|
|
65
|
+
const ocCompat = createOpenAICompatible({
|
|
66
|
+
name: entry?.label ?? 'opencode',
|
|
67
|
+
baseURL,
|
|
68
|
+
headers,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const id = resolvedModelId.toLowerCase();
|
|
72
|
+
if (id.includes('claude')) return ocAnthropic(resolvedModelId);
|
|
73
|
+
if (
|
|
74
|
+
id.includes('qwen3-coder') ||
|
|
75
|
+
id.includes('grok-code') ||
|
|
76
|
+
id.includes('kimi-k2')
|
|
77
|
+
)
|
|
78
|
+
return ocCompat(resolvedModelId);
|
|
79
|
+
return ocOpenAI(resolvedModelId);
|
|
80
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
2
|
+
import { createConditionalCachingFetch } from './anthropic-caching.ts';
|
|
3
|
+
|
|
4
|
+
export type OpenRouterProviderConfig = {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function isAnthropicModel(model: string): boolean {
|
|
9
|
+
const lower = model.toLowerCase();
|
|
10
|
+
return lower.includes('anthropic') || lower.includes('claude');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getOpenRouterInstance(
|
|
14
|
+
model?: string,
|
|
15
|
+
config?: OpenRouterProviderConfig,
|
|
16
|
+
) {
|
|
17
|
+
const apiKey = config?.apiKey ?? process.env.OPENROUTER_API_KEY ?? '';
|
|
18
|
+
const customFetch = model
|
|
19
|
+
? createConditionalCachingFetch(isAnthropicModel, model)
|
|
20
|
+
: undefined;
|
|
21
|
+
return createOpenRouter({ apiKey, fetch: customFetch });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createOpenRouterModel(
|
|
25
|
+
model: string,
|
|
26
|
+
config?: OpenRouterProviderConfig,
|
|
27
|
+
) {
|
|
28
|
+
const openrouter = getOpenRouterInstance(model, config);
|
|
29
|
+
return openrouter.chat(model);
|
|
30
|
+
}
|
|
@@ -7,6 +7,7 @@ import { svm } from 'x402/shared';
|
|
|
7
7
|
import nacl from 'tweetnacl';
|
|
8
8
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
9
9
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
10
|
+
import { addAnthropicCacheControl } from './anthropic-caching.ts';
|
|
10
11
|
|
|
11
12
|
const DEFAULT_BASE_URL = 'https://router.solforge.sh';
|
|
12
13
|
const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
@@ -148,87 +149,7 @@ export function createSolforgeFetch(
|
|
|
148
149
|
if (promptCacheRetention)
|
|
149
150
|
parsed.prompt_cache_retention = promptCacheRetention;
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
const MAX_MESSAGE_CACHE = 1;
|
|
153
|
-
let systemCacheUsed = 0;
|
|
154
|
-
let messageCacheUsed = 0;
|
|
155
|
-
|
|
156
|
-
if (parsed.system && Array.isArray(parsed.system)) {
|
|
157
|
-
parsed.system = parsed.system.map(
|
|
158
|
-
(
|
|
159
|
-
block: { type: string; cache_control?: unknown },
|
|
160
|
-
index: number,
|
|
161
|
-
) => {
|
|
162
|
-
if (block.cache_control) return block;
|
|
163
|
-
if (
|
|
164
|
-
systemCacheUsed < MAX_SYSTEM_CACHE &&
|
|
165
|
-
index === 0 &&
|
|
166
|
-
block.type === 'text'
|
|
167
|
-
) {
|
|
168
|
-
systemCacheUsed++;
|
|
169
|
-
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
170
|
-
}
|
|
171
|
-
return block;
|
|
172
|
-
},
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
177
|
-
const messageCount = parsed.messages.length;
|
|
178
|
-
parsed.messages = parsed.messages.map(
|
|
179
|
-
(
|
|
180
|
-
msg: {
|
|
181
|
-
role: string;
|
|
182
|
-
content: unknown;
|
|
183
|
-
[key: string]: unknown;
|
|
184
|
-
},
|
|
185
|
-
msgIndex: number,
|
|
186
|
-
) => {
|
|
187
|
-
const isLast = msgIndex === messageCount - 1;
|
|
188
|
-
|
|
189
|
-
if (Array.isArray(msg.content)) {
|
|
190
|
-
const blocks = msg.content as {
|
|
191
|
-
type: string;
|
|
192
|
-
cache_control?: unknown;
|
|
193
|
-
}[];
|
|
194
|
-
const content = blocks.map((block, blockIndex) => {
|
|
195
|
-
if (block.cache_control) return block;
|
|
196
|
-
if (
|
|
197
|
-
isLast &&
|
|
198
|
-
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
199
|
-
blockIndex === blocks.length - 1
|
|
200
|
-
) {
|
|
201
|
-
messageCacheUsed++;
|
|
202
|
-
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
203
|
-
}
|
|
204
|
-
return block;
|
|
205
|
-
});
|
|
206
|
-
return { ...msg, content };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
isLast &&
|
|
211
|
-
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
212
|
-
typeof msg.content === 'string'
|
|
213
|
-
) {
|
|
214
|
-
messageCacheUsed++;
|
|
215
|
-
return {
|
|
216
|
-
...msg,
|
|
217
|
-
content: [
|
|
218
|
-
{
|
|
219
|
-
type: 'text',
|
|
220
|
-
text: msg.content,
|
|
221
|
-
cache_control: { type: 'ephemeral' },
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return msg;
|
|
228
|
-
},
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
152
|
+
addAnthropicCacheControl(parsed);
|
|
232
153
|
body = JSON.stringify(parsed);
|
|
233
154
|
} catch {}
|
|
234
155
|
}
|
|
@@ -404,7 +325,21 @@ async function processSinglePayment(args: {
|
|
|
404
325
|
}): Promise<{ attempts: number; balance?: number | string }> {
|
|
405
326
|
args.callbacks.onPaymentSigning?.();
|
|
406
327
|
|
|
407
|
-
|
|
328
|
+
let paymentPayload: PaymentPayload;
|
|
329
|
+
try {
|
|
330
|
+
paymentPayload = await createPaymentPayload(args);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
333
|
+
const isInsufficientFunds =
|
|
334
|
+
errMsg.toLowerCase().includes('insufficient') ||
|
|
335
|
+
errMsg.toLowerCase().includes('not enough') ||
|
|
336
|
+
errMsg.toLowerCase().includes('balance');
|
|
337
|
+
const userMsg = isInsufficientFunds
|
|
338
|
+
? 'Insufficient USDC balance in wallet for payment'
|
|
339
|
+
: `Payment failed: ${errMsg}`;
|
|
340
|
+
args.callbacks.onPaymentError?.(userMsg);
|
|
341
|
+
throw new Error(`Solforge: ${userMsg}`);
|
|
342
|
+
}
|
|
408
343
|
const walletHeaders = args.buildWalletHeaders();
|
|
409
344
|
const headers = {
|
|
410
345
|
'Content-Type': 'application/json',
|
|
@@ -98,3 +98,24 @@ export function getFastModelForAuth(
|
|
|
98
98
|
|
|
99
99
|
return sorted[0]?.id ?? filteredModels[0]?.id;
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
export function getModelNpmBinding(
|
|
103
|
+
provider: ProviderId,
|
|
104
|
+
model: string,
|
|
105
|
+
): string | undefined {
|
|
106
|
+
const entry = catalog[provider];
|
|
107
|
+
if (!entry) return undefined;
|
|
108
|
+
const modelInfo = entry.models?.find((m) => m.id === model);
|
|
109
|
+
return modelInfo?.provider?.npm ?? entry.npm;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function isAnthropicBasedModel(
|
|
113
|
+
provider: ProviderId,
|
|
114
|
+
model: string,
|
|
115
|
+
): boolean {
|
|
116
|
+
if (provider === 'anthropic') return true;
|
|
117
|
+
const npm = getModelNpmBinding(provider, model);
|
|
118
|
+
if (npm === '@ai-sdk/anthropic') return true;
|
|
119
|
+
const lower = model.toLowerCase();
|
|
120
|
+
return lower.includes('claude') || lower.includes('anthropic');
|
|
121
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
2
|
+
import { catalog } from './catalog-merged.ts';
|
|
3
|
+
|
|
4
|
+
export type ZaiProviderConfig = {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function createZaiModel(model: string, config?: ZaiProviderConfig) {
|
|
9
|
+
const entry = catalog.zai;
|
|
10
|
+
const baseURL = entry?.api || 'https://api.z.ai/api/paas/v4';
|
|
11
|
+
const apiKey =
|
|
12
|
+
config?.apiKey ||
|
|
13
|
+
process.env.ZAI_API_KEY ||
|
|
14
|
+
process.env.ZHIPU_API_KEY ||
|
|
15
|
+
'';
|
|
16
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
17
|
+
|
|
18
|
+
const instance = createOpenAICompatible({
|
|
19
|
+
name: entry?.label ?? 'Z.AI',
|
|
20
|
+
baseURL,
|
|
21
|
+
headers,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return instance(model);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createZaiCodingModel(
|
|
28
|
+
model: string,
|
|
29
|
+
config?: ZaiProviderConfig,
|
|
30
|
+
) {
|
|
31
|
+
const entry = catalog['zai-coding'];
|
|
32
|
+
const baseURL = entry?.api || 'https://api.z.ai/api/coding/paas/v4';
|
|
33
|
+
const apiKey =
|
|
34
|
+
config?.apiKey ||
|
|
35
|
+
process.env.ZAI_API_KEY ||
|
|
36
|
+
process.env.ZHIPU_API_KEY ||
|
|
37
|
+
'';
|
|
38
|
+
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
39
|
+
|
|
40
|
+
const instance = createOpenAICompatible({
|
|
41
|
+
name: entry?.label ?? 'Z.AI Coding',
|
|
42
|
+
baseURL,
|
|
43
|
+
headers,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return instance(model);
|
|
47
|
+
}
|