@calliopelabs/cli 0.6.7 → 0.6.9
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/dist/agterm/agent-detection.d.ts +35 -0
- package/dist/agterm/agent-detection.d.ts.map +1 -0
- package/dist/agterm/agent-detection.js +94 -0
- package/dist/agterm/agent-detection.js.map +1 -0
- package/dist/agterm/cli-backend.d.ts +32 -0
- package/dist/agterm/cli-backend.d.ts.map +1 -0
- package/dist/agterm/cli-backend.js +193 -0
- package/dist/agterm/cli-backend.js.map +1 -0
- package/dist/agterm/index.d.ts +12 -0
- package/dist/agterm/index.d.ts.map +1 -0
- package/dist/agterm/index.js +15 -0
- package/dist/agterm/index.js.map +1 -0
- package/dist/agterm/orchestrator.d.ts +94 -0
- package/dist/agterm/orchestrator.d.ts.map +1 -0
- package/dist/agterm/orchestrator.js +302 -0
- package/dist/agterm/orchestrator.js.map +1 -0
- package/dist/agterm/tools.d.ts +24 -0
- package/dist/agterm/tools.d.ts.map +1 -0
- package/dist/agterm/tools.js +277 -0
- package/dist/agterm/tools.js.map +1 -0
- package/dist/agterm/types.d.ts +95 -0
- package/dist/agterm/types.d.ts.map +1 -0
- package/dist/agterm/types.js +38 -0
- package/dist/agterm/types.js.map +1 -0
- package/dist/bin.d.ts +2 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +18 -3
- package/dist/bin.js.map +1 -1
- package/dist/errors.d.ts +5 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +107 -4
- package/dist/errors.js.map +1 -1
- package/dist/plugins.d.ts +120 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +356 -0
- package/dist/plugins.js.map +1 -0
- package/dist/providers.js +208 -58
- package/dist/providers.js.map +1 -1
- package/dist/scope.d.ts +89 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +253 -0
- package/dist/scope.js.map +1 -0
- package/dist/storage.d.ts +23 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +83 -0
- package/dist/storage.js.map +1 -1
- package/dist/styles.d.ts +49 -83
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +140 -126
- package/dist/styles.js.map +1 -1
- package/dist/tools.d.ts +5 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +24 -10
- package/dist/tools.js.map +1 -1
- package/dist/ui-cli.d.ts +1 -0
- package/dist/ui-cli.d.ts.map +1 -1
- package/dist/ui-cli.js +877 -107
- package/dist/ui-cli.js.map +1 -1
- package/package.json +1 -1
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calliope CLI - Plugin System
|
|
3
|
+
*
|
|
4
|
+
* Enables community-extensible tools and providers.
|
|
5
|
+
* Plugins are loaded from ~/.calliope-cli/plugins/
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Plugin Manager
|
|
12
|
+
// ============================================================================
|
|
13
|
+
class PluginManager {
|
|
14
|
+
plugins = new Map();
|
|
15
|
+
pluginsDir;
|
|
16
|
+
constructor() {
|
|
17
|
+
this.pluginsDir = path.join(os.homedir(), '.calliope-cli', 'plugins');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the plugin system
|
|
21
|
+
*/
|
|
22
|
+
async init() {
|
|
23
|
+
// Ensure plugins directory exists
|
|
24
|
+
if (!fs.existsSync(this.pluginsDir)) {
|
|
25
|
+
fs.mkdirSync(this.pluginsDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
// Load all plugins
|
|
28
|
+
await this.loadAllPlugins();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load all plugins from the plugins directory
|
|
32
|
+
*/
|
|
33
|
+
async loadAllPlugins() {
|
|
34
|
+
if (!fs.existsSync(this.pluginsDir))
|
|
35
|
+
return;
|
|
36
|
+
const entries = fs.readdirSync(this.pluginsDir, { withFileTypes: true });
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
if (entry.isDirectory()) {
|
|
39
|
+
await this.loadPlugin(entry.name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load a single plugin by name
|
|
45
|
+
*/
|
|
46
|
+
async loadPlugin(name) {
|
|
47
|
+
const pluginPath = path.join(this.pluginsDir, name);
|
|
48
|
+
const manifestPath = path.join(pluginPath, 'plugin.json');
|
|
49
|
+
const indexPath = path.join(pluginPath, 'index.js');
|
|
50
|
+
try {
|
|
51
|
+
// Check for manifest
|
|
52
|
+
if (!fs.existsSync(manifestPath)) {
|
|
53
|
+
console.warn(`Plugin ${name}: Missing plugin.json`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Read manifest
|
|
57
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
58
|
+
// Check for entry point
|
|
59
|
+
if (!fs.existsSync(indexPath)) {
|
|
60
|
+
console.warn(`Plugin ${name}: Missing index.js`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
// Dynamic import the plugin
|
|
64
|
+
const pluginModule = await import(indexPath);
|
|
65
|
+
const plugin = pluginModule.default || pluginModule;
|
|
66
|
+
// Merge manifest with plugin
|
|
67
|
+
plugin.metadata = { ...manifest, ...plugin.metadata };
|
|
68
|
+
// Initialize plugin if it has init function
|
|
69
|
+
if (plugin.init) {
|
|
70
|
+
await plugin.init();
|
|
71
|
+
}
|
|
72
|
+
const loaded = {
|
|
73
|
+
id: name,
|
|
74
|
+
path: pluginPath,
|
|
75
|
+
plugin,
|
|
76
|
+
enabled: true,
|
|
77
|
+
loadedAt: new Date(),
|
|
78
|
+
};
|
|
79
|
+
this.plugins.set(name, loaded);
|
|
80
|
+
return loaded;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
84
|
+
console.error(`Failed to load plugin ${name}:`, errorMsg);
|
|
85
|
+
const failed = {
|
|
86
|
+
id: name,
|
|
87
|
+
path: pluginPath,
|
|
88
|
+
plugin: { metadata: { name, version: '0.0.0', description: 'Failed to load' } },
|
|
89
|
+
enabled: false,
|
|
90
|
+
loadedAt: new Date(),
|
|
91
|
+
error: errorMsg,
|
|
92
|
+
};
|
|
93
|
+
this.plugins.set(name, failed);
|
|
94
|
+
return failed;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Unload a plugin
|
|
99
|
+
*/
|
|
100
|
+
async unloadPlugin(name) {
|
|
101
|
+
const loaded = this.plugins.get(name);
|
|
102
|
+
if (!loaded)
|
|
103
|
+
return false;
|
|
104
|
+
try {
|
|
105
|
+
if (loaded.plugin.cleanup) {
|
|
106
|
+
await loaded.plugin.cleanup();
|
|
107
|
+
}
|
|
108
|
+
this.plugins.delete(name);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(`Failed to unload plugin ${name}:`, error);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Enable/disable a plugin
|
|
118
|
+
*/
|
|
119
|
+
setPluginEnabled(name, enabled) {
|
|
120
|
+
const loaded = this.plugins.get(name);
|
|
121
|
+
if (!loaded)
|
|
122
|
+
return false;
|
|
123
|
+
loaded.enabled = enabled;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get all loaded plugins
|
|
128
|
+
*/
|
|
129
|
+
getPlugins() {
|
|
130
|
+
return Array.from(this.plugins.values());
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get enabled plugins
|
|
134
|
+
*/
|
|
135
|
+
getEnabledPlugins() {
|
|
136
|
+
return Array.from(this.plugins.values()).filter(p => p.enabled && !p.error);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get a specific plugin
|
|
140
|
+
*/
|
|
141
|
+
getPlugin(name) {
|
|
142
|
+
return this.plugins.get(name);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get all tools from enabled plugins
|
|
146
|
+
*/
|
|
147
|
+
getPluginTools() {
|
|
148
|
+
const tools = [];
|
|
149
|
+
for (const loaded of this.getEnabledPlugins()) {
|
|
150
|
+
if (loaded.plugin.tools) {
|
|
151
|
+
for (const tool of loaded.plugin.tools) {
|
|
152
|
+
tools.push({
|
|
153
|
+
name: `${loaded.id}:${tool.name}`,
|
|
154
|
+
description: `[${loaded.id}] ${tool.description}`,
|
|
155
|
+
parameters: tool.parameters,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return tools;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Execute a plugin tool
|
|
164
|
+
*/
|
|
165
|
+
async executePluginTool(toolCall, cwd) {
|
|
166
|
+
const [pluginId, toolName] = toolCall.name.split(':');
|
|
167
|
+
const loaded = this.plugins.get(pluginId);
|
|
168
|
+
if (!loaded || !loaded.enabled) {
|
|
169
|
+
return {
|
|
170
|
+
toolCallId: toolCall.id,
|
|
171
|
+
result: `Plugin not found or disabled: ${pluginId}`,
|
|
172
|
+
isError: true,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const tool = loaded.plugin.tools?.find(t => t.name === toolName);
|
|
176
|
+
if (!tool) {
|
|
177
|
+
return {
|
|
178
|
+
toolCallId: toolCall.id,
|
|
179
|
+
result: `Tool not found: ${toolName} in plugin ${pluginId}`,
|
|
180
|
+
isError: true,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const result = await tool.execute(toolCall.arguments, cwd);
|
|
185
|
+
return { toolCallId: toolCall.id, result };
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
toolCallId: toolCall.id,
|
|
190
|
+
result: `Plugin tool error: ${error instanceof Error ? error.message : String(error)}`,
|
|
191
|
+
isError: true,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if a tool name is a plugin tool
|
|
197
|
+
*/
|
|
198
|
+
isPluginTool(name) {
|
|
199
|
+
return name.includes(':');
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Execute hooks for an event
|
|
203
|
+
*/
|
|
204
|
+
async executeHooks(event, context) {
|
|
205
|
+
for (const loaded of this.getEnabledPlugins()) {
|
|
206
|
+
if (loaded.plugin.hooks) {
|
|
207
|
+
for (const hook of loaded.plugin.hooks) {
|
|
208
|
+
if (hook.event === event) {
|
|
209
|
+
try {
|
|
210
|
+
await hook.handler(context);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error(`Plugin hook error (${loaded.id}):`, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get plugins directory path
|
|
222
|
+
*/
|
|
223
|
+
getPluginsDir() {
|
|
224
|
+
return this.pluginsDir;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Create a new plugin scaffold
|
|
228
|
+
*/
|
|
229
|
+
createPluginScaffold(name) {
|
|
230
|
+
const pluginPath = path.join(this.pluginsDir, name);
|
|
231
|
+
if (fs.existsSync(pluginPath)) {
|
|
232
|
+
throw new Error(`Plugin directory already exists: ${name}`);
|
|
233
|
+
}
|
|
234
|
+
fs.mkdirSync(pluginPath, { recursive: true });
|
|
235
|
+
// Create plugin.json
|
|
236
|
+
const manifest = {
|
|
237
|
+
name,
|
|
238
|
+
version: '1.0.0',
|
|
239
|
+
description: `${name} plugin for Calliope CLI`,
|
|
240
|
+
author: 'Your Name',
|
|
241
|
+
};
|
|
242
|
+
fs.writeFileSync(path.join(pluginPath, 'plugin.json'), JSON.stringify(manifest, null, 2));
|
|
243
|
+
// Create index.js
|
|
244
|
+
const indexContent = `/**
|
|
245
|
+
* ${name} - Calliope CLI Plugin
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
module.exports = {
|
|
249
|
+
// Plugin tools
|
|
250
|
+
tools: [
|
|
251
|
+
{
|
|
252
|
+
name: 'example',
|
|
253
|
+
description: 'An example tool from ${name} plugin',
|
|
254
|
+
parameters: {
|
|
255
|
+
type: 'object',
|
|
256
|
+
properties: {
|
|
257
|
+
message: {
|
|
258
|
+
type: 'string',
|
|
259
|
+
description: 'A message to echo',
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
required: ['message'],
|
|
263
|
+
},
|
|
264
|
+
execute: async (args, cwd) => {
|
|
265
|
+
return \`[${name}] Echo: \${args.message}\`;
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
|
|
270
|
+
// Plugin hooks (optional)
|
|
271
|
+
hooks: [
|
|
272
|
+
// {
|
|
273
|
+
// event: 'session-start',
|
|
274
|
+
// handler: async (context) => {
|
|
275
|
+
// console.log('${name} plugin: Session started');
|
|
276
|
+
// },
|
|
277
|
+
// },
|
|
278
|
+
],
|
|
279
|
+
|
|
280
|
+
// Called when plugin is loaded (optional)
|
|
281
|
+
init: async () => {
|
|
282
|
+
console.log('${name} plugin initialized');
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
// Called when plugin is unloaded (optional)
|
|
286
|
+
cleanup: async () => {
|
|
287
|
+
console.log('${name} plugin cleanup');
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
`;
|
|
291
|
+
fs.writeFileSync(path.join(pluginPath, 'index.js'), indexContent);
|
|
292
|
+
return pluginPath;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get formatted plugin list for display
|
|
296
|
+
*/
|
|
297
|
+
getPluginListFormatted() {
|
|
298
|
+
const plugins = this.getPlugins();
|
|
299
|
+
if (plugins.length === 0) {
|
|
300
|
+
return `No plugins installed.
|
|
301
|
+
|
|
302
|
+
To create a plugin:
|
|
303
|
+
/plugin create <name>
|
|
304
|
+
|
|
305
|
+
Plugins directory: ${this.pluginsDir}`;
|
|
306
|
+
}
|
|
307
|
+
const lines = ['Installed Plugins:', ''];
|
|
308
|
+
for (const p of plugins) {
|
|
309
|
+
const status = p.error ? '❌' : p.enabled ? '✅' : '⏸️';
|
|
310
|
+
const version = p.plugin.metadata.version;
|
|
311
|
+
const toolCount = p.plugin.tools?.length || 0;
|
|
312
|
+
lines.push(`${status} ${p.id} v${version}`);
|
|
313
|
+
lines.push(` ${p.plugin.metadata.description}`);
|
|
314
|
+
if (toolCount > 0) {
|
|
315
|
+
lines.push(` Tools: ${p.plugin.tools.map(t => t.name).join(', ')}`);
|
|
316
|
+
}
|
|
317
|
+
if (p.error) {
|
|
318
|
+
lines.push(` Error: ${p.error}`);
|
|
319
|
+
}
|
|
320
|
+
lines.push('');
|
|
321
|
+
}
|
|
322
|
+
return lines.join('\n');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// Singleton Instance
|
|
327
|
+
// ============================================================================
|
|
328
|
+
export const pluginManager = new PluginManager();
|
|
329
|
+
// ============================================================================
|
|
330
|
+
// Convenience Functions
|
|
331
|
+
// ============================================================================
|
|
332
|
+
export async function initPlugins() {
|
|
333
|
+
await pluginManager.init();
|
|
334
|
+
}
|
|
335
|
+
export function getPluginTools() {
|
|
336
|
+
return pluginManager.getPluginTools();
|
|
337
|
+
}
|
|
338
|
+
export function isPluginTool(name) {
|
|
339
|
+
return pluginManager.isPluginTool(name);
|
|
340
|
+
}
|
|
341
|
+
export async function executePluginTool(toolCall, cwd) {
|
|
342
|
+
return pluginManager.executePluginTool(toolCall, cwd);
|
|
343
|
+
}
|
|
344
|
+
export function getPluginList() {
|
|
345
|
+
return pluginManager.getPluginListFormatted();
|
|
346
|
+
}
|
|
347
|
+
export function createPlugin(name) {
|
|
348
|
+
return pluginManager.createPluginScaffold(name);
|
|
349
|
+
}
|
|
350
|
+
export async function reloadPlugins() {
|
|
351
|
+
await pluginManager.loadAllPlugins();
|
|
352
|
+
}
|
|
353
|
+
export function enablePlugin(name, enabled) {
|
|
354
|
+
return pluginManager.setPluginEnabled(name, enabled);
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"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"}
|
package/dist/providers.js
CHANGED
|
@@ -305,35 +305,89 @@ async function chatAnthropic(messages, tools, model, onToken) {
|
|
|
305
305
|
description: t.description,
|
|
306
306
|
input_schema: t.parameters,
|
|
307
307
|
}));
|
|
308
|
-
// Use streaming if callback provided
|
|
309
|
-
if (onToken
|
|
308
|
+
// Use streaming if callback provided - handles both text and tool calls
|
|
309
|
+
if (onToken) {
|
|
310
310
|
let content = '';
|
|
311
311
|
let inputTokens = 0;
|
|
312
312
|
let outputTokens = 0;
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
content
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
313
|
+
const toolCalls = [];
|
|
314
|
+
let currentToolId = '';
|
|
315
|
+
let currentToolName = '';
|
|
316
|
+
let currentToolInput = '';
|
|
317
|
+
let finishReason = 'stop';
|
|
318
|
+
try {
|
|
319
|
+
const stream = await client.messages.stream({
|
|
320
|
+
model,
|
|
321
|
+
max_tokens: MAX_TOKENS,
|
|
322
|
+
system: systemMessage ? getTextContent(systemMessage.content) : '',
|
|
323
|
+
messages: anthropicMessages,
|
|
324
|
+
tools: anthropicTools.length > 0 ? anthropicTools : undefined,
|
|
325
|
+
});
|
|
326
|
+
for await (const event of stream) {
|
|
327
|
+
if (event.type === 'content_block_start') {
|
|
328
|
+
if (event.content_block.type === 'tool_use') {
|
|
329
|
+
currentToolId = event.content_block.id;
|
|
330
|
+
currentToolName = event.content_block.name;
|
|
331
|
+
currentToolInput = '';
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (event.type === 'content_block_delta') {
|
|
335
|
+
if (event.delta.type === 'text_delta') {
|
|
336
|
+
const text = event.delta.text;
|
|
337
|
+
content += text;
|
|
338
|
+
onToken(text);
|
|
339
|
+
}
|
|
340
|
+
else if (event.delta.type === 'input_json_delta') {
|
|
341
|
+
currentToolInput += event.delta.partial_json;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else if (event.type === 'content_block_stop') {
|
|
345
|
+
if (currentToolId && currentToolName) {
|
|
346
|
+
try {
|
|
347
|
+
toolCalls.push({
|
|
348
|
+
id: currentToolId,
|
|
349
|
+
name: currentToolName,
|
|
350
|
+
arguments: JSON.parse(currentToolInput || '{}'),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
toolCalls.push({
|
|
355
|
+
id: currentToolId,
|
|
356
|
+
name: currentToolName,
|
|
357
|
+
arguments: {},
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
currentToolId = '';
|
|
361
|
+
currentToolName = '';
|
|
362
|
+
currentToolInput = '';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else if (event.type === 'message_delta') {
|
|
366
|
+
if (event.usage) {
|
|
367
|
+
outputTokens = event.usage.output_tokens;
|
|
368
|
+
}
|
|
369
|
+
if (event.delta.stop_reason === 'tool_use') {
|
|
370
|
+
finishReason = 'tool_use';
|
|
371
|
+
}
|
|
372
|
+
else if (event.delta.stop_reason === 'max_tokens') {
|
|
373
|
+
finishReason = 'length';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (event.type === 'message_start' && event.message.usage) {
|
|
377
|
+
inputTokens = event.message.usage.input_tokens;
|
|
378
|
+
}
|
|
330
379
|
}
|
|
380
|
+
return {
|
|
381
|
+
content,
|
|
382
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
383
|
+
finishReason,
|
|
384
|
+
usage: { inputTokens, outputTokens },
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
catch (streamError) {
|
|
388
|
+
// Fall back to non-streaming on error
|
|
389
|
+
console.error('Anthropic streaming failed, falling back:', streamError);
|
|
331
390
|
}
|
|
332
|
-
return {
|
|
333
|
-
content,
|
|
334
|
-
finishReason: 'stop',
|
|
335
|
-
usage: { inputTokens, outputTokens },
|
|
336
|
-
};
|
|
337
391
|
}
|
|
338
392
|
// Non-streaming request
|
|
339
393
|
const response = await client.messages.create({
|
|
@@ -452,26 +506,74 @@ async function chatOpenAI(messages, tools, model, onToken) {
|
|
|
452
506
|
const client = new OpenAI({ apiKey });
|
|
453
507
|
const openaiMessages = toOpenAIMessages(messages);
|
|
454
508
|
const openaiTools = toOpenAITools(tools);
|
|
455
|
-
// Use streaming if callback provided
|
|
456
|
-
|
|
509
|
+
// Use streaming if callback provided
|
|
510
|
+
// Stream text content while collecting tool calls
|
|
511
|
+
if (onToken) {
|
|
457
512
|
let content = '';
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
513
|
+
let toolCallDeltas = {};
|
|
514
|
+
let finishReason = 'stop';
|
|
515
|
+
try {
|
|
516
|
+
const stream = await client.chat.completions.create({
|
|
517
|
+
model,
|
|
518
|
+
messages: openaiMessages,
|
|
519
|
+
tools: openaiTools.length > 0 ? openaiTools : undefined,
|
|
520
|
+
max_tokens: MAX_TOKENS,
|
|
521
|
+
stream: true,
|
|
522
|
+
});
|
|
523
|
+
for await (const chunk of stream) {
|
|
524
|
+
const choice = chunk.choices[0];
|
|
525
|
+
if (!choice)
|
|
526
|
+
continue;
|
|
527
|
+
// Handle text content
|
|
528
|
+
const textDelta = choice.delta?.content;
|
|
529
|
+
if (textDelta) {
|
|
530
|
+
content += textDelta;
|
|
531
|
+
onToken(textDelta);
|
|
532
|
+
}
|
|
533
|
+
// Handle tool calls (collect deltas)
|
|
534
|
+
const toolCallDelta = choice.delta?.tool_calls;
|
|
535
|
+
if (toolCallDelta) {
|
|
536
|
+
for (const tc of toolCallDelta) {
|
|
537
|
+
if (!toolCallDeltas[tc.index]) {
|
|
538
|
+
toolCallDeltas[tc.index] = { id: '', name: '', arguments: '' };
|
|
539
|
+
}
|
|
540
|
+
if (tc.id)
|
|
541
|
+
toolCallDeltas[tc.index].id = tc.id;
|
|
542
|
+
if (tc.function?.name)
|
|
543
|
+
toolCallDeltas[tc.index].name = tc.function.name;
|
|
544
|
+
if (tc.function?.arguments)
|
|
545
|
+
toolCallDeltas[tc.index].arguments += tc.function.arguments;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
// Track finish reason
|
|
549
|
+
if (choice.finish_reason === 'tool_calls') {
|
|
550
|
+
finishReason = 'tool_use';
|
|
551
|
+
}
|
|
552
|
+
else if (choice.finish_reason === 'length') {
|
|
553
|
+
finishReason = 'length';
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// Convert tool call deltas to tool calls
|
|
557
|
+
const toolCalls = Object.values(toolCallDeltas)
|
|
558
|
+
.filter(tc => tc.id && tc.name)
|
|
559
|
+
.map(tc => ({
|
|
560
|
+
id: tc.id,
|
|
561
|
+
name: tc.name,
|
|
562
|
+
arguments: JSON.parse(tc.arguments || '{}'),
|
|
563
|
+
}));
|
|
564
|
+
if (toolCalls.length > 0) {
|
|
565
|
+
finishReason = 'tool_use';
|
|
469
566
|
}
|
|
567
|
+
return {
|
|
568
|
+
content,
|
|
569
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
570
|
+
finishReason,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
catch (streamError) {
|
|
574
|
+
// Fall back to non-streaming on error
|
|
575
|
+
console.error('Streaming failed, falling back to non-streaming:', streamError);
|
|
470
576
|
}
|
|
471
|
-
return {
|
|
472
|
-
content,
|
|
473
|
-
finishReason: 'stop',
|
|
474
|
-
};
|
|
475
577
|
}
|
|
476
578
|
// Non-streaming request
|
|
477
579
|
const response = await client.chat.completions.create({
|
|
@@ -530,26 +632,74 @@ async function chatOpenAICompatible(provider, messages, tools, model, onToken) {
|
|
|
530
632
|
const client = new OpenAI({ apiKey, baseURL });
|
|
531
633
|
const openaiMessages = toOpenAIMessages(messages);
|
|
532
634
|
const openaiTools = toOpenAITools(tools);
|
|
533
|
-
// Use streaming if callback provided
|
|
534
|
-
|
|
635
|
+
// Use streaming if callback provided
|
|
636
|
+
// Stream text content while collecting tool calls
|
|
637
|
+
if (onToken) {
|
|
535
638
|
let content = '';
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
639
|
+
let toolCallDeltas = {};
|
|
640
|
+
let finishReason = 'stop';
|
|
641
|
+
try {
|
|
642
|
+
const stream = await client.chat.completions.create({
|
|
643
|
+
model,
|
|
644
|
+
messages: openaiMessages,
|
|
645
|
+
tools: openaiTools.length > 0 ? openaiTools : undefined,
|
|
646
|
+
max_tokens: MAX_TOKENS,
|
|
647
|
+
stream: true,
|
|
648
|
+
});
|
|
649
|
+
for await (const chunk of stream) {
|
|
650
|
+
const choice = chunk.choices[0];
|
|
651
|
+
if (!choice)
|
|
652
|
+
continue;
|
|
653
|
+
// Handle text content
|
|
654
|
+
const textDelta = choice.delta?.content;
|
|
655
|
+
if (textDelta) {
|
|
656
|
+
content += textDelta;
|
|
657
|
+
onToken(textDelta);
|
|
658
|
+
}
|
|
659
|
+
// Handle tool calls (collect deltas)
|
|
660
|
+
const toolCallDelta = choice.delta?.tool_calls;
|
|
661
|
+
if (toolCallDelta) {
|
|
662
|
+
for (const tc of toolCallDelta) {
|
|
663
|
+
if (!toolCallDeltas[tc.index]) {
|
|
664
|
+
toolCallDeltas[tc.index] = { id: '', name: '', arguments: '' };
|
|
665
|
+
}
|
|
666
|
+
if (tc.id)
|
|
667
|
+
toolCallDeltas[tc.index].id = tc.id;
|
|
668
|
+
if (tc.function?.name)
|
|
669
|
+
toolCallDeltas[tc.index].name = tc.function.name;
|
|
670
|
+
if (tc.function?.arguments)
|
|
671
|
+
toolCallDeltas[tc.index].arguments += tc.function.arguments;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
// Track finish reason
|
|
675
|
+
if (choice.finish_reason === 'tool_calls') {
|
|
676
|
+
finishReason = 'tool_use';
|
|
677
|
+
}
|
|
678
|
+
else if (choice.finish_reason === 'length') {
|
|
679
|
+
finishReason = 'length';
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// Convert tool call deltas to tool calls
|
|
683
|
+
const toolCalls = Object.values(toolCallDeltas)
|
|
684
|
+
.filter(tc => tc.id && tc.name)
|
|
685
|
+
.map(tc => ({
|
|
686
|
+
id: tc.id,
|
|
687
|
+
name: tc.name,
|
|
688
|
+
arguments: JSON.parse(tc.arguments || '{}'),
|
|
689
|
+
}));
|
|
690
|
+
if (toolCalls.length > 0) {
|
|
691
|
+
finishReason = 'tool_use';
|
|
547
692
|
}
|
|
693
|
+
return {
|
|
694
|
+
content,
|
|
695
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
696
|
+
finishReason,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
catch (streamError) {
|
|
700
|
+
// Fall back to non-streaming on error
|
|
701
|
+
console.error('Streaming failed, falling back to non-streaming:', streamError);
|
|
548
702
|
}
|
|
549
|
-
return {
|
|
550
|
-
content,
|
|
551
|
-
finishReason: 'stop',
|
|
552
|
-
};
|
|
553
703
|
}
|
|
554
704
|
// Non-streaming request
|
|
555
705
|
const response = await client.chat.completions.create({
|