create-bk-agent 0.23.4 → 0.23.6

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.
Files changed (45) hide show
  1. package/README.md +0 -2
  2. package/dist/src/add/modules/knowledge.js +18 -18
  3. package/dist/src/add/modules/server.js +1 -1
  4. package/dist/src/add/patcher.js +106 -68
  5. package/dist/src/scaffold.js +29 -16
  6. package/package.json +2 -2
  7. package/templates/README.md +26 -0
  8. package/templates/base/autonomous/package.json +1 -1
  9. package/templates/base/autonomous/src/index.ts +83 -83
  10. package/templates/base/coding/package.json +1 -1
  11. package/templates/base/enterprise/package.json +1 -1
  12. package/templates/base/minimal/package.json +1 -1
  13. package/templates/base/minimal/src/index.ts +43 -43
  14. package/templates/base/multi-agent/package.json +2 -2
  15. package/templates/base/multi-agent/src/engine.ts +78 -78
  16. package/templates/base/single/package.json +2 -2
  17. package/templates/base/single/src/engine.ts +67 -67
  18. package/templates/autonomous/.env.example +0 -8
  19. package/templates/autonomous/package.json +0 -25
  20. package/templates/autonomous/src/index.ts +0 -64
  21. package/templates/autonomous/tsconfig.json +0 -16
  22. package/templates/base/multi-agent/src/providers/openai-compatible.ts +0 -104
  23. package/templates/base/single/src/providers/openai-compatible.ts +0 -104
  24. package/templates/coding/.env.example +0 -8
  25. package/templates/coding/package.json +0 -26
  26. package/templates/coding/src/index.ts +0 -42
  27. package/templates/coding/tsconfig.json +0 -17
  28. package/templates/domain-agent/.env.example +0 -7
  29. package/templates/domain-agent/package.json +0 -27
  30. package/templates/domain-agent/src/agents/orchestrator.ts +0 -22
  31. package/templates/domain-agent/src/agents/specialist-a.ts +0 -17
  32. package/templates/domain-agent/src/agents/specialist-b.ts +0 -16
  33. package/templates/domain-agent/src/config.ts +0 -19
  34. package/templates/domain-agent/src/engine.ts +0 -49
  35. package/templates/domain-agent/src/index.ts +0 -27
  36. package/templates/domain-agent/src/providers/openai-compatible.ts +0 -104
  37. package/templates/domain-agent/src/tools/example-tools.ts +0 -28
  38. package/templates/domain-agent/tsconfig.json +0 -17
  39. package/templates/mcp-server/package.json +0 -30
  40. package/templates/mcp-server/src/server.ts +0 -40
  41. package/templates/mcp-server/src/tools/example.ts +0 -10
  42. package/templates/mcp-server/tsconfig.json +0 -16
  43. package/templates/minimal/package.json +0 -25
  44. package/templates/minimal/src/index.ts +0 -40
  45. package/templates/minimal/tsconfig.json +0 -16
@@ -1,43 +1,43 @@
1
- import 'dotenv/config';
2
- import { createBaseEngine } from '@backendkit-labs/agent-core';
3
- import type { AgentProfile, LLMProvider } from '@backendkit-labs/agent-core';
4
-
5
- // ── Provider ──────────────────────────────────────────────────────────────────
6
- // TODO: replace with the actual provider for your chosen LLM
7
- // e.g. AnthropicProvider from @backendkit-labs/agent-coding
8
- // or implement LLMProvider interface directly
9
-
10
- const myProvider: LLMProvider = {
11
- chat: async (_messages, _tools, callbacks) => {
12
- // TODO: call your LLM API here
13
- callbacks.onDone({ role: 'assistant', content: 'Hello from my agent!' });
14
- },
15
- };
16
-
17
- // ── Agent ─────────────────────────────────────────────────────────────────────
18
-
19
- const myAgent: AgentProfile = {
20
- id: 'my-agent',
21
- name: 'My Agent',
22
- icon: '🤖',
23
- description: 'TODO: describe your agent',
24
- allowedTools: [],
25
- delegatesTo: [],
26
- systemPrompt: 'TODO: write your system prompt here.',
27
- };
28
-
29
- // ── Engine ────────────────────────────────────────────────────────────────────
30
-
31
- const engine = createBaseEngine({
32
- providers: { default: myProvider },
33
- defaultProvider: 'default',
34
- agents: [myAgent],
35
- defaultAgent: 'my-agent',
36
- });
37
-
38
- // ── Run ───────────────────────────────────────────────────────────────────────
39
-
40
- void (async () => {
41
- const input = process.argv[2] ?? 'Hello';
42
- await engine.run(input);
43
- })();
1
+ import 'dotenv/config';
2
+ import { createBaseEngine } from '@backendkit-labs/agent-core';
3
+ import type { AgentProfile, LLMProvider } from '@backendkit-labs/agent-core';
4
+
5
+ // ── Provider ──────────────────────────────────────────────────────────────────
6
+ // TODO: replace with the actual provider for your chosen LLM
7
+ // e.g. AnthropicProvider from @backendkit-labs/agent-coding
8
+ // or implement LLMProvider interface directly
9
+
10
+ const myProvider: LLMProvider = {
11
+ chat: async (_messages, _tools, callbacks) => {
12
+ // TODO: call your LLM API here
13
+ callbacks.onDone({ role: 'assistant', content: 'Hello from my agent!' });
14
+ },
15
+ };
16
+
17
+ // ── Agent ─────────────────────────────────────────────────────────────────────
18
+
19
+ const myAgent: AgentProfile = {
20
+ id: 'my-agent',
21
+ name: 'My Agent',
22
+ icon: '🤖',
23
+ description: 'TODO: describe your agent',
24
+ allowedTools: [], // the tools this agent may call, by name; empty means none (a tool that is registered but not listed here is refused)
25
+ delegatesTo: [],
26
+ systemPrompt: 'TODO: write your system prompt here.',
27
+ };
28
+
29
+ // ── Engine ────────────────────────────────────────────────────────────────────
30
+
31
+ const engine = createBaseEngine({
32
+ providers: { default: myProvider },
33
+ defaultProvider: 'default',
34
+ agents: [myAgent],
35
+ defaultAgent: 'my-agent',
36
+ });
37
+
38
+ // ── Run ───────────────────────────────────────────────────────────────────────
39
+
40
+ void (async () => {
41
+ const input = process.argv[2] ?? 'Hello';
42
+ await engine.run(input);
43
+ })();
@@ -12,9 +12,9 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@backendkit-labs/agent-core": "^0.36.0",
15
+ "@backendkit-labs/agent-core": "^0.38.0",
16
+ "@backendkit-labs/agent-providers": "^0.3.3",
16
17
  "dotenv": "^16.0.0",
17
- "openai": "^4.0.0",
18
18
  "zod": "^4.6.6"
19
19
  },
20
20
  "devDependencies": {
@@ -1,78 +1,78 @@
1
- import * as os from 'os';
2
- import * as path from 'path';
3
- import {
4
- AgentEngine,
5
- AgentRegistry,
6
- ToolRegistry,
7
- ProviderRegistry,
8
- CallbackTransport,
9
- createMemorySystem,
10
- } from '@backendkit-labs/agent-core';
11
- import { OpenAICompatibleProvider } from './providers/openai-compatible';
12
- import { loadConfig } from './config';
13
- import { ORCHESTRATOR_PROFILE } from './agents/orchestrator';
14
- import { SPECIALIST_A_PROFILE } from './agents/specialist-a';
15
- import { SPECIALIST_B_PROFILE } from './agents/specialist-b';
16
- import { exampleAction, exampleQuery } from './tools/example-tools';
17
-
18
- // ── Persistent storage paths ──────────────────────────────────────────────────
19
- // All state lives in ~/.my-agent/ so it survives restarts.
20
-
21
- const APP_DIR = path.join(os.homedir(), '.my-agent');
22
- const HISTORY = path.join(APP_DIR, 'history.json');
23
- const MEM_DIR = path.join(APP_DIR, 'memory');
24
-
25
- // ── Memory system ─────────────────────────────────────────────────────────────
26
- // Three-tier: episodic (past experiences) + semantic (knowledge) + procedural (patterns).
27
- // buildContextBlock() auto-injects relevant memories into each agent turn.
28
-
29
- const memory = createMemorySystem({
30
- episodic: { provider: 'json', path: path.join(MEM_DIR, 'episodes.json') },
31
- semantic: { provider: 'json', path: path.join(MEM_DIR, 'knowledge.json') },
32
- procedural: { provider: 'json', path: path.join(MEM_DIR, 'patterns.json') },
33
- retention: { maxEpisodes: 500 },
34
- });
35
-
36
- export function createEngine(transport: CallbackTransport): AgentEngine {
37
- const config = loadConfig();
38
-
39
- const tools = new ToolRegistry();
40
- tools
41
- .register(exampleAction)
42
- .register(exampleQuery);
43
- // Register memory tools so agents can explicitly save/recall memories
44
- for (const t of memory.getTools()) tools.register(t);
45
- // TODO: register more tools
46
-
47
- const agents = new AgentRegistry();
48
- agents
49
- .register(ORCHESTRATOR_PROFILE)
50
- .register(SPECIALIST_A_PROFILE)
51
- .register(SPECIALIST_B_PROFILE);
52
- // TODO: register more specialists
53
-
54
- const providers = new ProviderRegistry();
55
- providers.register('default', new OpenAICompatibleProvider({
56
- apiKey: config.llmApiKey,
57
- baseUrl: config.llmBaseUrl,
58
- model: config.llmModel,
59
- }));
60
-
61
- return new AgentEngine({
62
- model: { provider: 'default', id: config.llmModel },
63
- agents,
64
- tools,
65
- providers,
66
- defaultProvider: 'default',
67
- defaultAgentId: 'orchestrator',
68
- transport,
69
- maxIterations: 30,
70
- iterationMode: 'auto',
71
- // Conversation history — persists between runs
72
- historyPath: HISTORY,
73
- maxContextMessages: 100,
74
- // Long-term memory — injects relevant context each turn
75
- memorySystem: memory,
76
- additionalRoots: [APP_DIR],
77
- });
78
- }
1
+ import * as os from 'os';
2
+ import * as path from 'path';
3
+ import {
4
+ AgentEngine,
5
+ AgentRegistry,
6
+ ToolRegistry,
7
+ ProviderRegistry,
8
+ CallbackTransport,
9
+ createMemorySystem,
10
+ } from '@backendkit-labs/agent-core';
11
+ import { OpenAICompatibleProvider } from '@backendkit-labs/agent-providers';
12
+ import { loadConfig } from './config';
13
+ import { ORCHESTRATOR_PROFILE } from './agents/orchestrator';
14
+ import { SPECIALIST_A_PROFILE } from './agents/specialist-a';
15
+ import { SPECIALIST_B_PROFILE } from './agents/specialist-b';
16
+ import { exampleAction, exampleQuery } from './tools/example-tools';
17
+
18
+ // ── Persistent storage paths ──────────────────────────────────────────────────
19
+ // All state lives in ~/.my-agent/ so it survives restarts.
20
+
21
+ const APP_DIR = path.join(os.homedir(), '.my-agent');
22
+ const HISTORY = path.join(APP_DIR, 'history.json');
23
+ const MEM_DIR = path.join(APP_DIR, 'memory');
24
+
25
+ // ── Memory system ─────────────────────────────────────────────────────────────
26
+ // Three-tier: episodic (past experiences) + semantic (knowledge) + procedural (patterns).
27
+ // buildContextBlock() auto-injects relevant memories into each agent turn.
28
+
29
+ const memory = createMemorySystem({
30
+ episodic: { provider: 'json', path: path.join(MEM_DIR, 'episodes.json') },
31
+ semantic: { provider: 'json', path: path.join(MEM_DIR, 'knowledge.json') },
32
+ procedural: { provider: 'json', path: path.join(MEM_DIR, 'patterns.json') },
33
+ retention: { maxEpisodes: 500 },
34
+ });
35
+
36
+ export function createEngine(transport: CallbackTransport): AgentEngine {
37
+ const config = loadConfig();
38
+
39
+ const tools = new ToolRegistry();
40
+ tools
41
+ .register(exampleAction)
42
+ .register(exampleQuery);
43
+ // Register memory tools so agents can explicitly save/recall memories
44
+ for (const t of memory.getTools()) tools.register(t);
45
+ // TODO: register more tools
46
+
47
+ const agents = new AgentRegistry();
48
+ agents
49
+ .register(ORCHESTRATOR_PROFILE)
50
+ .register(SPECIALIST_A_PROFILE)
51
+ .register(SPECIALIST_B_PROFILE);
52
+ // TODO: register more specialists
53
+
54
+ const providers = new ProviderRegistry();
55
+ providers.register('default', new OpenAICompatibleProvider({
56
+ apiKey: config.llmApiKey,
57
+ baseUrl: config.llmBaseUrl,
58
+ model: config.llmModel,
59
+ }));
60
+
61
+ return new AgentEngine({
62
+ model: { provider: 'default', id: config.llmModel },
63
+ agents,
64
+ tools,
65
+ providers,
66
+ defaultProvider: 'default',
67
+ defaultAgentId: 'orchestrator',
68
+ transport,
69
+ maxIterations: 30,
70
+ iterationMode: 'auto',
71
+ // Conversation history — persists between runs
72
+ historyPath: HISTORY,
73
+ maxContextMessages: 100,
74
+ // Long-term memory — injects relevant context each turn
75
+ memorySystem: memory,
76
+ additionalRoots: [APP_DIR],
77
+ });
78
+ }
@@ -12,9 +12,9 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@backendkit-labs/agent-core": "^0.36.0",
15
+ "@backendkit-labs/agent-core": "^0.38.0",
16
+ "@backendkit-labs/agent-providers": "^0.3.3",
16
17
  "dotenv": "^16.0.0",
17
- "openai": "^4.0.0",
18
18
  "zod": "^4.6.6"
19
19
  },
20
20
  "devDependencies": {
@@ -1,67 +1,67 @@
1
- import * as os from 'os';
2
- import * as path from 'path';
3
- import {
4
- AgentEngine,
5
- AgentRegistry,
6
- ToolRegistry,
7
- ProviderRegistry,
8
- CallbackTransport,
9
- createMemorySystem,
10
- } from '@backendkit-labs/agent-core';
11
- import { OpenAICompatibleProvider } from './providers/openai-compatible';
12
- import { loadConfig } from './config';
13
- import { MY_AGENT_PROFILE } from './agents/my-agent';
14
- import { exampleAction, exampleQuery } from './tools/example-tools';
15
-
16
- // ── Persistent storage paths ──────────────────────────────────────────────────
17
-
18
- const APP_DIR = path.join(os.homedir(), '.my-agent');
19
- const HISTORY = path.join(APP_DIR, 'history.json');
20
- const MEM_DIR = path.join(APP_DIR, 'memory');
21
-
22
- // ── Memory system ─────────────────────────────────────────────────────────────
23
-
24
- const memory = createMemorySystem({
25
- episodic: { provider: 'json', path: path.join(MEM_DIR, 'episodes.json') },
26
- semantic: { provider: 'json', path: path.join(MEM_DIR, 'knowledge.json') },
27
- procedural: { provider: 'json', path: path.join(MEM_DIR, 'patterns.json') },
28
- retention: { maxEpisodes: 500 },
29
- });
30
-
31
- export function createEngine(transport: CallbackTransport): AgentEngine {
32
- const config = loadConfig();
33
-
34
- const tools = new ToolRegistry();
35
- tools
36
- .register(exampleAction)
37
- .register(exampleQuery);
38
- for (const t of memory.getTools()) tools.register(t);
39
- // TODO: register more tools
40
-
41
- const agents = new AgentRegistry();
42
- agents.register(MY_AGENT_PROFILE);
43
- // TODO: register more specialists
44
-
45
- const providers = new ProviderRegistry();
46
- providers.register('default', new OpenAICompatibleProvider({
47
- apiKey: config.llmApiKey,
48
- baseUrl: config.llmBaseUrl,
49
- model: config.llmModel,
50
- }));
51
-
52
- return new AgentEngine({
53
- model: { provider: 'default', id: config.llmModel },
54
- agents,
55
- tools,
56
- providers,
57
- defaultProvider: 'default',
58
- defaultAgentId: 'my-agent',
59
- transport,
60
- maxIterations: 20,
61
- iterationMode: 'auto',
62
- historyPath: HISTORY,
63
- maxContextMessages: 100,
64
- memorySystem: memory,
65
- additionalRoots: [APP_DIR],
66
- });
67
- }
1
+ import * as os from 'os';
2
+ import * as path from 'path';
3
+ import {
4
+ AgentEngine,
5
+ AgentRegistry,
6
+ ToolRegistry,
7
+ ProviderRegistry,
8
+ CallbackTransport,
9
+ createMemorySystem,
10
+ } from '@backendkit-labs/agent-core';
11
+ import { OpenAICompatibleProvider } from '@backendkit-labs/agent-providers';
12
+ import { loadConfig } from './config';
13
+ import { MY_AGENT_PROFILE } from './agents/my-agent';
14
+ import { exampleAction, exampleQuery } from './tools/example-tools';
15
+
16
+ // ── Persistent storage paths ──────────────────────────────────────────────────
17
+
18
+ const APP_DIR = path.join(os.homedir(), '.my-agent');
19
+ const HISTORY = path.join(APP_DIR, 'history.json');
20
+ const MEM_DIR = path.join(APP_DIR, 'memory');
21
+
22
+ // ── Memory system ─────────────────────────────────────────────────────────────
23
+
24
+ const memory = createMemorySystem({
25
+ episodic: { provider: 'json', path: path.join(MEM_DIR, 'episodes.json') },
26
+ semantic: { provider: 'json', path: path.join(MEM_DIR, 'knowledge.json') },
27
+ procedural: { provider: 'json', path: path.join(MEM_DIR, 'patterns.json') },
28
+ retention: { maxEpisodes: 500 },
29
+ });
30
+
31
+ export function createEngine(transport: CallbackTransport): AgentEngine {
32
+ const config = loadConfig();
33
+
34
+ const tools = new ToolRegistry();
35
+ tools
36
+ .register(exampleAction)
37
+ .register(exampleQuery);
38
+ for (const t of memory.getTools()) tools.register(t);
39
+ // TODO: register more tools
40
+
41
+ const agents = new AgentRegistry();
42
+ agents.register(MY_AGENT_PROFILE);
43
+ // TODO: register more specialists
44
+
45
+ const providers = new ProviderRegistry();
46
+ providers.register('default', new OpenAICompatibleProvider({
47
+ apiKey: config.llmApiKey,
48
+ baseUrl: config.llmBaseUrl,
49
+ model: config.llmModel,
50
+ }));
51
+
52
+ return new AgentEngine({
53
+ model: { provider: 'default', id: config.llmModel },
54
+ agents,
55
+ tools,
56
+ providers,
57
+ defaultProvider: 'default',
58
+ defaultAgentId: 'my-agent',
59
+ transport,
60
+ maxIterations: 20,
61
+ iterationMode: 'auto',
62
+ historyPath: HISTORY,
63
+ maxContextMessages: 100,
64
+ memorySystem: memory,
65
+ additionalRoots: [APP_DIR],
66
+ });
67
+ }
@@ -1,8 +0,0 @@
1
- ANTHROPIC_API_KEY=sk-ant-...
2
- # OPENAI_API_KEY=sk-...
3
-
4
- LLM_PROVIDER=anthropic
5
- LLM_MODEL=claude-sonnet-4-6
6
-
7
- WEBHOOK_PORT=3000
8
- # WEBHOOK_SECRET=your-secret # for GitHub/Slack webhook validation
@@ -1,25 +0,0 @@
1
- {
2
- "name": "my-autonomous-agent",
3
- "version": "0.1.0",
4
- "private": true,
5
- "description": "Autonomous agent — activates on cron, webhooks, or custom events",
6
- "main": "dist/index.js",
7
- "scripts": {
8
- "dev": "ts-node src/index.ts",
9
- "build": "tsc",
10
- "start": "node dist/index.js",
11
- "typecheck": "tsc --noEmit"
12
- },
13
- "dependencies": {
14
- "@backendkit-labs/agent-core": "^0.36.0",
15
- "dotenv": "^16.0.0"
16
- },
17
- "devDependencies": {
18
- "@types/node": "^20.0.0",
19
- "ts-node": "^10.9.0",
20
- "typescript": "^5.4.0"
21
- },
22
- "engines": {
23
- "node": ">=20.0.0"
24
- }
25
- }
@@ -1,64 +0,0 @@
1
- import 'dotenv/config';
2
- import { createAutonomousAgent } from '@backendkit-labs/agent-core';
3
- import type { AgentProfile } from '@backendkit-labs/agent-core';
4
-
5
- // ── Agent definition ──────────────────────────────────────────────────────────
6
-
7
- const monitorAgent: AgentProfile = {
8
- id: 'monitor',
9
- name: 'Monitor',
10
- icon: '👁',
11
- description: 'Monitors events and executes scheduled tasks',
12
- allowedTools: [],
13
- delegatesTo: [],
14
- systemPrompt: `You are an autonomous monitor agent.
15
- // TODO: describe what this agent monitors and what actions it takes.
16
- When activated by a trigger, analyze the input and take appropriate action.`,
17
- };
18
-
19
- // ── Provider ──────────────────────────────────────────────────────────────────
20
-
21
- const provider = process.env.LLM_PROVIDER ?? 'anthropic';
22
- const apiKey = process.env.ANTHROPIC_API_KEY ?? process.env.OPENAI_API_KEY ?? '';
23
-
24
- if (!apiKey) { console.error('Missing API key'); process.exit(1); }
25
-
26
- // ── Autonomous agent ──────────────────────────────────────────────────────────
27
-
28
- const agent = createAutonomousAgent({
29
- providers: { [provider]: { apiKey } },
30
- defaultProvider: provider,
31
- agents: [monitorAgent],
32
- defaultAgent: 'monitor',
33
- goal: 'TODO: describe the agent goal',
34
-
35
- triggers: [
36
- // Run every day at 9:00 AM
37
- {
38
- type: 'schedule',
39
- cron: '0 9 * * *',
40
- task: 'Run daily check and report findings',
41
- },
42
-
43
- // Listen for webhook events (e.g. from GitHub, Slack, etc.)
44
- {
45
- type: 'webhook',
46
- path: '/events',
47
- // secret: process.env.WEBHOOK_SECRET, // uncomment to validate signatures
48
- },
49
-
50
- // TODO: add more triggers as needed
51
- // { type: 'event', topic: 'my.custom.event' },
52
- ],
53
-
54
- webhookPort: parseInt(process.env.WEBHOOK_PORT ?? '3000'),
55
- });
56
-
57
- // ── Start ─────────────────────────────────────────────────────────────────────
58
-
59
- await agent.start();
60
- console.log('Autonomous agent started.');
61
- console.log('Webhook listening on port', process.env.WEBHOOK_PORT ?? 3000);
62
-
63
- process.on('SIGTERM', async () => { await agent.stop(); process.exit(0); });
64
- process.on('SIGINT', async () => { await agent.stop(); process.exit(0); });
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "commonjs",
5
- "moduleResolution": "node16",
6
- "lib": ["ES2022"],
7
- "types": ["node"],
8
- "outDir": "dist",
9
- "rootDir": "src",
10
- "strict": true,
11
- "skipLibCheck": true,
12
- "esModuleInterop": true
13
- },
14
- "include": ["src/**/*"],
15
- "exclude": ["node_modules", "dist"]
16
- }
@@ -1,104 +0,0 @@
1
- import OpenAI from 'openai';
2
- import type { LLMProvider, LLMMessage, LLMStreamCallbacks, ToolDefinition } from '@backendkit-labs/agent-core';
3
-
4
- export interface OpenAICompatibleProviderOptions {
5
- apiKey: string;
6
- baseUrl: string;
7
- model: string;
8
- maxRetries?: number;
9
- }
10
-
11
- function isRetryable(err: unknown): boolean {
12
- if (err && typeof err === 'object' && 'status' in err) {
13
- const s = (err as { status: number }).status;
14
- return s === 429 || s >= 500;
15
- }
16
- return false;
17
- }
18
-
19
- const sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));
20
-
21
- export class OpenAICompatibleProvider implements LLMProvider {
22
- private readonly client: OpenAI;
23
- private readonly model: string;
24
- private readonly maxRetries: number;
25
-
26
- constructor(opts: OpenAICompatibleProviderOptions) {
27
- this.client = new OpenAI({ apiKey: opts.apiKey, baseURL: opts.baseUrl, maxRetries: 0 });
28
- this.model = opts.model;
29
- this.maxRetries = opts.maxRetries ?? 3;
30
- }
31
-
32
- async chat(messages: LLMMessage[], tools: ToolDefinition[], callbacks: LLMStreamCallbacks): Promise<void> {
33
- const oaiTools = tools.map(t => ({
34
- type: 'function' as const,
35
- function: { name: t.name, description: t.description, parameters: t.parameters },
36
- })) as OpenAI.Chat.ChatCompletionTool[];
37
-
38
- let attempt = 0;
39
- let emitted = false;
40
-
41
- while (attempt < this.maxRetries) {
42
- attempt++;
43
- try {
44
- await this.stream(messages, oaiTools, {
45
- ...callbacks,
46
- onChunk: (d) => { emitted = true; callbacks.onChunk?.(d); },
47
- });
48
- return;
49
- } catch (err) {
50
- if (emitted || !isRetryable(err) || attempt >= this.maxRetries) {
51
- callbacks.onError(err instanceof Error ? err : new Error(String(err)));
52
- return;
53
- }
54
- await sleep(Math.min(1000 * 2 ** attempt, 16_000));
55
- }
56
- }
57
- }
58
-
59
- private async stream(
60
- messages: LLMMessage[],
61
- oaiTools: OpenAI.Chat.ChatCompletionTool[],
62
- callbacks: LLMStreamCallbacks,
63
- ): Promise<void> {
64
- const stream = await this.client.chat.completions.create({
65
- model: this.model,
66
- messages: messages as OpenAI.Chat.ChatCompletionMessageParam[],
67
- tools: oaiTools.length ? oaiTools : undefined,
68
- tool_choice: oaiTools.length ? 'auto' : undefined,
69
- stream: true,
70
- stream_options: { include_usage: true },
71
- });
72
-
73
- let content = '';
74
- const toolBuffers = new Map<number, { id: string; name: string; args: string }>();
75
-
76
- for await (const chunk of stream) {
77
- if (chunk.usage) callbacks.onMetrics?.(chunk.usage.prompt_tokens, chunk.usage.completion_tokens);
78
- const delta = chunk.choices[0]?.delta;
79
- if (!delta) continue;
80
- if (delta.content) { content += delta.content; callbacks.onChunk?.(delta.content); }
81
- if (delta.tool_calls) {
82
- for (const tc of delta.tool_calls) {
83
- if (!toolBuffers.has(tc.index)) toolBuffers.set(tc.index, { id: '', name: '', args: '' });
84
- const buf = toolBuffers.get(tc.index)!;
85
- if (tc.id) buf.id = tc.id;
86
- if (tc.function?.name) buf.name += tc.function.name;
87
- if (tc.function?.arguments) buf.args += tc.function.arguments;
88
- }
89
- }
90
- }
91
-
92
- const toolCalls = [...toolBuffers.entries()]
93
- .sort(([a], [b]) => a - b)
94
- .map(([, b]) => ({ id: b.id, type: 'function' as const, function: { name: b.name, arguments: b.args } }));
95
-
96
- for (const tc of toolCalls) callbacks.onToolCall?.(tc.function.name, tc.function.arguments, tc.id);
97
-
98
- callbacks.onDone({
99
- role: 'assistant',
100
- content: content || null,
101
- tool_calls: toolCalls.length ? toolCalls : undefined,
102
- });
103
- }
104
- }