@honeybee-ai/incubator 1.1.0

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 (245) hide show
  1. package/README.md +586 -0
  2. package/dashboard/dist/assets/index-DFb8p7xI.js +9 -0
  3. package/dashboard/dist/assets/index-RKiEoHEo.css +1 -0
  4. package/dashboard/dist/index.html +13 -0
  5. package/dist/agent/acp/claim-manager.d.ts +24 -0
  6. package/dist/agent/acp/claim-manager.js +64 -0
  7. package/dist/agent/acp/claim-manager.js.map +1 -0
  8. package/dist/agent/acp/direct-runtime.d.ts +90 -0
  9. package/dist/agent/acp/direct-runtime.js +364 -0
  10. package/dist/agent/acp/direct-runtime.js.map +1 -0
  11. package/dist/agent/acp/event-client.d.ts +20 -0
  12. package/dist/agent/acp/event-client.js +60 -0
  13. package/dist/agent/acp/event-client.js.map +1 -0
  14. package/dist/agent/acp/event-matcher.d.ts +13 -0
  15. package/dist/agent/acp/event-matcher.js +31 -0
  16. package/dist/agent/acp/event-matcher.js.map +1 -0
  17. package/dist/agent/acp/progress.d.ts +23 -0
  18. package/dist/agent/acp/progress.js +54 -0
  19. package/dist/agent/acp/progress.js.map +1 -0
  20. package/dist/agent/acp/runtime.d.ts +156 -0
  21. package/dist/agent/acp/runtime.js +337 -0
  22. package/dist/agent/acp/runtime.js.map +1 -0
  23. package/dist/agent/acp/ws-event-client.d.ts +64 -0
  24. package/dist/agent/acp/ws-event-client.js +263 -0
  25. package/dist/agent/acp/ws-event-client.js.map +1 -0
  26. package/dist/agent/agent.d.ts +60 -0
  27. package/dist/agent/agent.js +121 -0
  28. package/dist/agent/agent.js.map +1 -0
  29. package/dist/agent/cli.d.ts +2 -0
  30. package/dist/agent/cli.js +311 -0
  31. package/dist/agent/cli.js.map +1 -0
  32. package/dist/agent/mcp-client.d.ts +37 -0
  33. package/dist/agent/mcp-client.js +92 -0
  34. package/dist/agent/mcp-client.js.map +1 -0
  35. package/dist/agent/mock-runner.d.ts +14 -0
  36. package/dist/agent/mock-runner.js +159 -0
  37. package/dist/agent/mock-runner.js.map +1 -0
  38. package/dist/agent/native-client.d.ts +18 -0
  39. package/dist/agent/native-client.js +42 -0
  40. package/dist/agent/native-client.js.map +1 -0
  41. package/dist/agent/prompt.d.ts +45 -0
  42. package/dist/agent/prompt.js +115 -0
  43. package/dist/agent/prompt.js.map +1 -0
  44. package/dist/agent/providers.d.ts +25 -0
  45. package/dist/agent/providers.js +696 -0
  46. package/dist/agent/providers.js.map +1 -0
  47. package/dist/agent/runner.d.ts +15 -0
  48. package/dist/agent/runner.js +625 -0
  49. package/dist/agent/runner.js.map +1 -0
  50. package/dist/agent/tool-client.d.ts +12 -0
  51. package/dist/agent/tool-client.js +2 -0
  52. package/dist/agent/tool-client.js.map +1 -0
  53. package/dist/agent/types.d.ts +116 -0
  54. package/dist/agent/types.js +2 -0
  55. package/dist/agent/types.js.map +1 -0
  56. package/dist/agent-pool.d.ts +44 -0
  57. package/dist/agent-pool.js +228 -0
  58. package/dist/agent-pool.js.map +1 -0
  59. package/dist/bin.d.ts +2 -0
  60. package/dist/bin.js +7 -0
  61. package/dist/bin.js.map +1 -0
  62. package/dist/bus.d.ts +24 -0
  63. package/dist/bus.js +79 -0
  64. package/dist/bus.js.map +1 -0
  65. package/dist/dances.d.ts +73 -0
  66. package/dist/dances.js +122 -0
  67. package/dist/dances.js.map +1 -0
  68. package/dist/guard.d.ts +52 -0
  69. package/dist/guard.js +210 -0
  70. package/dist/guard.js.map +1 -0
  71. package/dist/heartbeat.d.ts +41 -0
  72. package/dist/heartbeat.js +104 -0
  73. package/dist/heartbeat.js.map +1 -0
  74. package/dist/honeycomb.d.ts +63 -0
  75. package/dist/honeycomb.js +222 -0
  76. package/dist/honeycomb.js.map +1 -0
  77. package/dist/index.d.ts +2 -0
  78. package/dist/index.js +601 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/integrations/config.d.ts +15 -0
  81. package/dist/integrations/config.js +62 -0
  82. package/dist/integrations/config.js.map +1 -0
  83. package/dist/integrations/index.d.ts +4 -0
  84. package/dist/integrations/index.js +4 -0
  85. package/dist/integrations/index.js.map +1 -0
  86. package/dist/integrations/loader.d.ts +8 -0
  87. package/dist/integrations/loader.js +27 -0
  88. package/dist/integrations/loader.js.map +1 -0
  89. package/dist/integrations/manager.d.ts +29 -0
  90. package/dist/integrations/manager.js +108 -0
  91. package/dist/integrations/manager.js.map +1 -0
  92. package/dist/log.d.ts +25 -0
  93. package/dist/log.js +67 -0
  94. package/dist/log.js.map +1 -0
  95. package/dist/namespaces.d.ts +28 -0
  96. package/dist/namespaces.js +100 -0
  97. package/dist/namespaces.js.map +1 -0
  98. package/dist/orchestrator.d.ts +119 -0
  99. package/dist/orchestrator.js +463 -0
  100. package/dist/orchestrator.js.map +1 -0
  101. package/dist/persistence.d.ts +7 -0
  102. package/dist/persistence.js +62 -0
  103. package/dist/persistence.js.map +1 -0
  104. package/dist/plugins/index.d.ts +2 -0
  105. package/dist/plugins/index.js +3 -0
  106. package/dist/plugins/index.js.map +1 -0
  107. package/dist/plugins/loader.d.ts +12 -0
  108. package/dist/plugins/loader.js +122 -0
  109. package/dist/plugins/loader.js.map +1 -0
  110. package/dist/plugins/manager.d.ts +76 -0
  111. package/dist/plugins/manager.js +238 -0
  112. package/dist/plugins/manager.js.map +1 -0
  113. package/dist/propolis/guard.d.ts +23 -0
  114. package/dist/propolis/guard.js +49 -0
  115. package/dist/propolis/guard.js.map +1 -0
  116. package/dist/propolis/tools/types.d.ts +9 -0
  117. package/dist/propolis/tools/types.js +9 -0
  118. package/dist/propolis/tools/types.js.map +1 -0
  119. package/dist/rest.d.ts +4 -0
  120. package/dist/rest.js +962 -0
  121. package/dist/rest.js.map +1 -0
  122. package/dist/run-watcher.d.ts +20 -0
  123. package/dist/run-watcher.js +74 -0
  124. package/dist/run-watcher.js.map +1 -0
  125. package/dist/server.d.ts +17 -0
  126. package/dist/server.js +412 -0
  127. package/dist/server.js.map +1 -0
  128. package/dist/stores/backend.d.ts +15 -0
  129. package/dist/stores/backend.js +28 -0
  130. package/dist/stores/backend.js.map +1 -0
  131. package/dist/stores/claims.d.ts +14 -0
  132. package/dist/stores/claims.js +77 -0
  133. package/dist/stores/claims.js.map +1 -0
  134. package/dist/stores/conflicts.d.ts +10 -0
  135. package/dist/stores/conflicts.js +39 -0
  136. package/dist/stores/conflicts.js.map +1 -0
  137. package/dist/stores/control.d.ts +37 -0
  138. package/dist/stores/control.js +105 -0
  139. package/dist/stores/control.js.map +1 -0
  140. package/dist/stores/discoveries.d.ts +11 -0
  141. package/dist/stores/discoveries.js +45 -0
  142. package/dist/stores/discoveries.js.map +1 -0
  143. package/dist/stores/events.d.ts +14 -0
  144. package/dist/stores/events.js +42 -0
  145. package/dist/stores/events.js.map +1 -0
  146. package/dist/stores/help.d.ts +11 -0
  147. package/dist/stores/help.js +46 -0
  148. package/dist/stores/help.js.map +1 -0
  149. package/dist/stores/interfaces.d.ts +125 -0
  150. package/dist/stores/interfaces.js +2 -0
  151. package/dist/stores/interfaces.js.map +1 -0
  152. package/dist/stores/messages.d.ts +8 -0
  153. package/dist/stores/messages.js +29 -0
  154. package/dist/stores/messages.js.map +1 -0
  155. package/dist/stores/progress.d.ts +8 -0
  156. package/dist/stores/progress.js +21 -0
  157. package/dist/stores/progress.js.map +1 -0
  158. package/dist/stores/proposals.d.ts +11 -0
  159. package/dist/stores/proposals.js +46 -0
  160. package/dist/stores/proposals.js.map +1 -0
  161. package/dist/stores/redis/claims.d.ts +16 -0
  162. package/dist/stores/redis/claims.js +126 -0
  163. package/dist/stores/redis/claims.js.map +1 -0
  164. package/dist/stores/redis/db.d.ts +39 -0
  165. package/dist/stores/redis/db.js +34 -0
  166. package/dist/stores/redis/db.js.map +1 -0
  167. package/dist/stores/redis/discoveries.d.ts +13 -0
  168. package/dist/stores/redis/discoveries.js +54 -0
  169. package/dist/stores/redis/discoveries.js.map +1 -0
  170. package/dist/stores/redis/events.d.ts +17 -0
  171. package/dist/stores/redis/events.js +57 -0
  172. package/dist/stores/redis/events.js.map +1 -0
  173. package/dist/stores/redis/index.d.ts +3 -0
  174. package/dist/stores/redis/index.js +31 -0
  175. package/dist/stores/redis/index.js.map +1 -0
  176. package/dist/stores/redis/state.d.ts +14 -0
  177. package/dist/stores/redis/state.js +83 -0
  178. package/dist/stores/redis/state.js.map +1 -0
  179. package/dist/stores/reinforcements.d.ts +11 -0
  180. package/dist/stores/reinforcements.js +42 -0
  181. package/dist/stores/reinforcements.js.map +1 -0
  182. package/dist/stores/roles.d.ts +9 -0
  183. package/dist/stores/roles.js +22 -0
  184. package/dist/stores/roles.js.map +1 -0
  185. package/dist/stores/runs.d.ts +15 -0
  186. package/dist/stores/runs.js +50 -0
  187. package/dist/stores/runs.js.map +1 -0
  188. package/dist/stores/sqlite/claims.d.ts +17 -0
  189. package/dist/stores/sqlite/claims.js +121 -0
  190. package/dist/stores/sqlite/claims.js.map +1 -0
  191. package/dist/stores/sqlite/db.d.ts +16 -0
  192. package/dist/stores/sqlite/db.js +77 -0
  193. package/dist/stores/sqlite/db.js.map +1 -0
  194. package/dist/stores/sqlite/discoveries.d.ts +14 -0
  195. package/dist/stores/sqlite/discoveries.js +66 -0
  196. package/dist/stores/sqlite/discoveries.js.map +1 -0
  197. package/dist/stores/sqlite/events.d.ts +16 -0
  198. package/dist/stores/sqlite/events.js +75 -0
  199. package/dist/stores/sqlite/events.js.map +1 -0
  200. package/dist/stores/sqlite/index.d.ts +2 -0
  201. package/dist/stores/sqlite/index.js +33 -0
  202. package/dist/stores/sqlite/index.js.map +1 -0
  203. package/dist/stores/sqlite/state.d.ts +15 -0
  204. package/dist/stores/sqlite/state.js +99 -0
  205. package/dist/stores/sqlite/state.js.map +1 -0
  206. package/dist/stores/state.d.ts +11 -0
  207. package/dist/stores/state.js +67 -0
  208. package/dist/stores/state.js.map +1 -0
  209. package/dist/transports/broker.d.ts +20 -0
  210. package/dist/transports/broker.js +102 -0
  211. package/dist/transports/broker.js.map +1 -0
  212. package/dist/transports/index.d.ts +3 -0
  213. package/dist/transports/index.js +3 -0
  214. package/dist/transports/index.js.map +1 -0
  215. package/dist/transports/ipc.d.ts +26 -0
  216. package/dist/transports/ipc.js +93 -0
  217. package/dist/transports/ipc.js.map +1 -0
  218. package/dist/transports/types.d.ts +39 -0
  219. package/dist/transports/types.js +8 -0
  220. package/dist/transports/types.js.map +1 -0
  221. package/dist/types.d.ts +45 -0
  222. package/dist/types.js +2 -0
  223. package/dist/types.js.map +1 -0
  224. package/dist/utils.d.ts +3 -0
  225. package/dist/utils.js +36 -0
  226. package/dist/utils.js.map +1 -0
  227. package/dist/waggle/client.d.ts +16 -0
  228. package/dist/waggle/client.js +28 -0
  229. package/dist/waggle/client.js.map +1 -0
  230. package/dist/waggle/compound.d.ts +22 -0
  231. package/dist/waggle/compound.js +194 -0
  232. package/dist/waggle/compound.js.map +1 -0
  233. package/dist/waggle/index.d.ts +25 -0
  234. package/dist/waggle/index.js +77 -0
  235. package/dist/waggle/index.js.map +1 -0
  236. package/dist/waggle/types.d.ts +54 -0
  237. package/dist/waggle/types.js +2 -0
  238. package/dist/waggle/types.js.map +1 -0
  239. package/dist/webhooks.d.ts +26 -0
  240. package/dist/webhooks.js +79 -0
  241. package/dist/webhooks.js.map +1 -0
  242. package/dist/ws.d.ts +33 -0
  243. package/dist/ws.js +195 -0
  244. package/dist/ws.js.map +1 -0
  245. package/package.json +122 -0
@@ -0,0 +1,696 @@
1
+ export const PROVIDER_CATALOG = {
2
+ cerebras: { tier: 'fast', baseUrl: 'https://api.cerebras.ai', format: 'openai', defaultModel: 'llama-3.3-70b', envVar: 'CEREBRAS_API_KEY', cost: { prompt: 0.10, completion: 0.10 } },
3
+ groq: { tier: 'fast', baseUrl: 'https://api.groq.com/openai', format: 'openai', defaultModel: 'llama-3.3-70b-versatile', envVar: 'GROQ_API_KEY', cost: { prompt: 0.27, completion: 0.27 } },
4
+ openai: { tier: 'smart', baseUrl: 'https://api.openai.com', format: 'openai', defaultModel: 'gpt-4o-mini', envVar: 'OPENAI_API_KEY', cost: { prompt: 2.50, completion: 10.00 } },
5
+ anthropic: { tier: 'smart', baseUrl: 'https://api.anthropic.com', format: 'anthropic', defaultModel: 'claude-sonnet-4-5-20250929', envVar: 'ANTHROPIC_API_KEY', cost: { prompt: 3.00, completion: 15.00 } },
6
+ ollama: { tier: 'local', baseUrl: 'http://localhost:11434', format: 'ollama', defaultModel: 'qwen3:32b', envVar: 'OLLAMA_HOST', cost: { prompt: 0, completion: 0 } },
7
+ };
8
+ const PROVIDER_ALIASES = {
9
+ fast: 'cerebras',
10
+ smart: 'openai',
11
+ local: 'ollama',
12
+ };
13
+ // ─── Provider resolution ────────────────────────────────────────────
14
+ export function resolveProvider(shorthand) {
15
+ // Resolve aliases: "fast" → "cerebras", "smart" → "openai", "local" → "ollama"
16
+ let resolved = shorthand;
17
+ if (PROVIDER_ALIASES[resolved]) {
18
+ resolved = PROVIDER_ALIASES[resolved];
19
+ }
20
+ const slash = resolved.indexOf('/');
21
+ let rawName;
22
+ let model;
23
+ if (slash === -1) {
24
+ // Provider-only: "cerebras" → "cerebras/llama-3.3-70b"
25
+ rawName = resolved;
26
+ const catalog = PROVIDER_CATALOG[rawName];
27
+ if (!catalog) {
28
+ throw new Error(`Unknown provider "${rawName}". Known: ${Object.keys(PROVIDER_CATALOG).join(', ')}. Aliases: ${Object.keys(PROVIDER_ALIASES).join(', ')}.`);
29
+ }
30
+ model = catalog.defaultModel;
31
+ }
32
+ else {
33
+ rawName = resolved.slice(0, slash);
34
+ // Resolve alias in provider part too: "fast/custom-model"
35
+ if (PROVIDER_ALIASES[rawName]) {
36
+ rawName = PROVIDER_ALIASES[rawName];
37
+ }
38
+ model = resolved.slice(slash + 1);
39
+ if (!model) {
40
+ const catalog = PROVIDER_CATALOG[rawName];
41
+ model = catalog?.defaultModel || 'llama-3.3-70b';
42
+ }
43
+ }
44
+ const providerName = rawName;
45
+ const catalog = PROVIDER_CATALOG[providerName];
46
+ const type = providerName === 'ollama' ? 'ollama'
47
+ : providerName === 'anthropic' ? 'anthropic'
48
+ : 'openai'; // groq, cerebras, together, fireworks, etc. are all OpenAI-compatible
49
+ // Support OLLAMA_HOST env var
50
+ let baseUrl = catalog?.baseUrl ?? PROVIDER_CATALOG.openai.baseUrl;
51
+ if (type === 'ollama' && process.env.OLLAMA_HOST) {
52
+ const host = process.env.OLLAMA_HOST;
53
+ baseUrl = host.startsWith('http') ? host : `http://${host}`;
54
+ }
55
+ // API key from env var (catalog knows the var name)
56
+ const envVar = catalog?.envVar;
57
+ let apiKey;
58
+ if (envVar && process.env[envVar]) {
59
+ apiKey = process.env[envVar];
60
+ }
61
+ return { type, baseUrl, apiKey, model, providerName };
62
+ }
63
+ // ─── Connection checks ──────────────────────────────────────────────
64
+ export async function checkConnection(provider) {
65
+ try {
66
+ if (provider.type === 'ollama') {
67
+ const res = await fetch(`${provider.baseUrl}/api/tags`);
68
+ return res.ok;
69
+ }
70
+ if (provider.type === 'anthropic') {
71
+ const res = await fetch(`${provider.baseUrl}/v1/messages`, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'x-api-key': provider.apiKey ?? '',
75
+ 'anthropic-version': '2023-06-01',
76
+ 'Content-Type': 'application/json',
77
+ },
78
+ body: JSON.stringify({ model: provider.model, max_tokens: 1, messages: [{ role: 'user', content: 'ping' }] }),
79
+ });
80
+ return res.status !== 0;
81
+ }
82
+ const headers = {};
83
+ if (provider.apiKey)
84
+ headers['Authorization'] = `Bearer ${provider.apiKey}`;
85
+ const res = await fetch(`${provider.baseUrl}/v1/models`, { headers });
86
+ return res.ok || res.status === 401;
87
+ }
88
+ catch {
89
+ return false;
90
+ }
91
+ }
92
+ export async function checkModel(provider) {
93
+ if (provider.type === 'ollama') {
94
+ try {
95
+ const res = await fetch(`${provider.baseUrl}/api/tags`);
96
+ if (!res.ok)
97
+ return false;
98
+ const data = await res.json();
99
+ const models = data.models ?? [];
100
+ return models.some(m => m.name === provider.model || m.name === `${provider.model}:latest`);
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ // ─── Provider SDK clients (lazy-initialized, cached per API key) ──────
109
+ const _sdkModules = {};
110
+ const _sdkClientCache = new Map();
111
+ async function getCerebrasClient(apiKey) {
112
+ const key = `cerebras:${apiKey}`;
113
+ if (!_sdkClientCache.has(key)) {
114
+ if (!_sdkModules.cerebras) {
115
+ _sdkModules.cerebras = (await import('@cerebras/cerebras_cloud_sdk')).default;
116
+ }
117
+ _sdkClientCache.set(key, new _sdkModules.cerebras({ apiKey }));
118
+ }
119
+ return _sdkClientCache.get(key);
120
+ }
121
+ async function getGroqClient(apiKey) {
122
+ const key = `groq:${apiKey}`;
123
+ if (!_sdkClientCache.has(key)) {
124
+ if (!_sdkModules.groq) {
125
+ _sdkModules.groq = (await import('groq-sdk')).default;
126
+ }
127
+ _sdkClientCache.set(key, new _sdkModules.groq({ apiKey }));
128
+ }
129
+ return _sdkClientCache.get(key);
130
+ }
131
+ async function getAnthropicClient(apiKey) {
132
+ const key = `anthropic:${apiKey}`;
133
+ if (!_sdkClientCache.has(key)) {
134
+ if (!_sdkModules.anthropic) {
135
+ _sdkModules.anthropic = (await import('@anthropic-ai/sdk')).default;
136
+ }
137
+ _sdkClientCache.set(key, new _sdkModules.anthropic({ apiKey }));
138
+ }
139
+ return _sdkClientCache.get(key);
140
+ }
141
+ // ─── Chat completion ────────────────────────────────────────────────
142
+ const MAX_RETRIES = 5;
143
+ const BASE_DELAY_MS = 2000;
144
+ const ZERO_USAGE = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
145
+ export async function chatCompletion(provider, messages, tools, temperature, options) {
146
+ const opts = { temperature, ...options };
147
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
148
+ try {
149
+ // Route to native SDKs when available, fall back to raw fetch
150
+ if (provider.type === 'anthropic') {
151
+ if (provider.apiKey) {
152
+ return await anthropicSdkCompletion(provider, messages, tools, opts.temperature);
153
+ }
154
+ return await anthropicCompletion(provider, messages, tools, opts.temperature);
155
+ }
156
+ if (provider.type === 'ollama') {
157
+ return await ollamaCompletion(provider, messages, tools, opts.temperature);
158
+ }
159
+ // Cerebras and Groq get native SDK handling
160
+ if (provider.providerName === 'cerebras' && provider.apiKey) {
161
+ return await cerebrasSdkCompletion(provider, messages, tools, opts.temperature);
162
+ }
163
+ if (provider.providerName === 'groq' && provider.apiKey) {
164
+ return await groqSdkCompletion(provider, messages, tools, opts.temperature);
165
+ }
166
+ return await openaiCompletion(provider, messages, tools, opts.temperature, opts.disableReasoning, opts);
167
+ }
168
+ catch (err) {
169
+ const msg = err instanceof Error ? err.message : String(err);
170
+ const isRateLimit = msg.includes('(429)') || msg.toLowerCase().includes('rate limit');
171
+ if (!isRateLimit || attempt === MAX_RETRIES)
172
+ throw err;
173
+ // Exponential backoff with jitter
174
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 1000;
175
+ console.error(`[hive] Rate limited, retrying in ${(delay / 1000).toFixed(1)}s (attempt ${attempt + 1}/${MAX_RETRIES})...`);
176
+ await new Promise(r => setTimeout(r, delay));
177
+ }
178
+ }
179
+ throw new Error('Unreachable');
180
+ }
181
+ // ─── Ollama (native /api/chat) ──────────────────────────────────────
182
+ async function ollamaCompletion(provider, messages, tools, temperature) {
183
+ const body = {
184
+ model: provider.model,
185
+ messages: messages.map(m => {
186
+ const msg = { role: m.role, content: m.content ?? '' };
187
+ if (m.tool_calls)
188
+ msg.tool_calls = m.tool_calls;
189
+ if (m.tool_call_id)
190
+ msg.tool_call_id = m.tool_call_id;
191
+ return msg;
192
+ }),
193
+ stream: false,
194
+ options: {},
195
+ };
196
+ if (tools.length > 0)
197
+ body.tools = tools;
198
+ if (temperature !== undefined)
199
+ body.options.temperature = temperature;
200
+ const res = await fetch(`${provider.baseUrl}/api/chat`, {
201
+ method: 'POST',
202
+ headers: { 'Content-Type': 'application/json' },
203
+ body: JSON.stringify(body),
204
+ });
205
+ if (!res.ok) {
206
+ const text = await res.text();
207
+ throw new Error(`Ollama error (${res.status}): ${text}`);
208
+ }
209
+ const data = await res.json();
210
+ const msg = data.message;
211
+ if (!msg)
212
+ throw new Error('Ollama returned no message');
213
+ const message = {
214
+ role: 'assistant',
215
+ content: msg.content || null,
216
+ };
217
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
218
+ message.tool_calls = msg.tool_calls.map((tc, i) => ({
219
+ id: `ollama_${Date.now()}_${i}`,
220
+ type: 'function',
221
+ function: {
222
+ name: tc.function.name,
223
+ arguments: tc.function.arguments,
224
+ },
225
+ }));
226
+ }
227
+ else if (message.content) {
228
+ // Fallback: some models emit tool calls as text instead of using native tool_calls.
229
+ const extracted = extractToolCallsFromText(message.content, tools);
230
+ if (extracted.length > 0) {
231
+ message.tool_calls = extracted;
232
+ message.content = null;
233
+ }
234
+ }
235
+ const promptTokens = data.prompt_eval_count ?? 0;
236
+ const completionTokens = data.eval_count ?? 0;
237
+ const usage = {
238
+ promptTokens,
239
+ completionTokens,
240
+ totalTokens: promptTokens + completionTokens,
241
+ };
242
+ return { message, usage };
243
+ }
244
+ // ─── OpenAI-compatible (/v1/chat/completions) ───────────────────────
245
+ async function openaiCompletion(provider, messages, tools, temperature, disableReasoning, opts) {
246
+ const headers = { 'Content-Type': 'application/json' };
247
+ if (provider.apiKey)
248
+ headers['Authorization'] = `Bearer ${provider.apiKey}`;
249
+ const body = {
250
+ model: provider.model,
251
+ messages: messages.map(m => {
252
+ const msg = { role: m.role };
253
+ if (m.content !== undefined)
254
+ msg.content = m.content;
255
+ if (m.tool_calls)
256
+ msg.tool_calls = m.tool_calls.map(tc => ({
257
+ id: tc.id,
258
+ type: 'function',
259
+ function: {
260
+ name: tc.function.name,
261
+ arguments: typeof tc.function.arguments === 'string'
262
+ ? tc.function.arguments
263
+ : JSON.stringify(tc.function.arguments),
264
+ },
265
+ }));
266
+ if (m.tool_call_id)
267
+ msg.tool_call_id = m.tool_call_id;
268
+ return msg;
269
+ }),
270
+ };
271
+ if (tools.length > 0) {
272
+ body.tools = tools;
273
+ body.parallel_tool_calls = true;
274
+ }
275
+ if (temperature !== undefined)
276
+ body.temperature = temperature;
277
+ if (opts?.reasoningEffort)
278
+ body.reasoning_effort = opts.reasoningEffort;
279
+ const res = await fetch(`${provider.baseUrl}/v1/chat/completions`, {
280
+ method: 'POST',
281
+ headers,
282
+ body: JSON.stringify(body),
283
+ });
284
+ if (!res.ok) {
285
+ const text = await res.text();
286
+ throw new Error(`OpenAI error (${res.status}): ${text}`);
287
+ }
288
+ const data = await res.json();
289
+ const choice = data.choices?.[0]?.message;
290
+ if (!choice)
291
+ throw new Error('OpenAI returned no choices');
292
+ // Strip Qwen <think>...</think> blocks from content
293
+ let content = choice.content ?? null;
294
+ if (content) {
295
+ content = content.replace(/<think>[\s\S]*?<\/think>/g, '').trim() || null;
296
+ }
297
+ const message = {
298
+ role: 'assistant',
299
+ content,
300
+ };
301
+ if (choice.tool_calls && choice.tool_calls.length > 0) {
302
+ message.tool_calls = choice.tool_calls.map(tc => ({
303
+ id: tc.id,
304
+ type: 'function',
305
+ function: {
306
+ name: tc.function.name,
307
+ arguments: parseArguments(tc.function.arguments),
308
+ },
309
+ }));
310
+ }
311
+ else if (message.content && tools.length > 0) {
312
+ // Fallback: some models emit tool calls as text instead of structured tool_calls
313
+ const extracted = extractToolCallsFromText(message.content, tools);
314
+ if (extracted.length > 0) {
315
+ message.tool_calls = extracted;
316
+ message.content = null;
317
+ }
318
+ }
319
+ const usage = {
320
+ promptTokens: data.usage?.prompt_tokens ?? 0,
321
+ completionTokens: data.usage?.completion_tokens ?? 0,
322
+ totalTokens: data.usage?.total_tokens ?? 0,
323
+ };
324
+ return { message, usage };
325
+ }
326
+ async function anthropicCompletion(provider, messages, tools, temperature) {
327
+ let system;
328
+ const anthropicMessages = [];
329
+ for (const msg of messages) {
330
+ if (msg.role === 'system') {
331
+ system = msg.content ?? undefined;
332
+ continue;
333
+ }
334
+ if (msg.role === 'assistant') {
335
+ const content = [];
336
+ if (msg.content)
337
+ content.push({ type: 'text', text: msg.content });
338
+ if (msg.tool_calls) {
339
+ for (const tc of msg.tool_calls) {
340
+ content.push({
341
+ type: 'tool_use',
342
+ id: tc.id ?? `call_${Date.now()}`,
343
+ name: tc.function.name,
344
+ input: typeof tc.function.arguments === 'string'
345
+ ? parseArguments(tc.function.arguments)
346
+ : tc.function.arguments,
347
+ });
348
+ }
349
+ }
350
+ anthropicMessages.push({ role: 'assistant', content });
351
+ continue;
352
+ }
353
+ if (msg.role === 'tool') {
354
+ const last = anthropicMessages[anthropicMessages.length - 1];
355
+ const toolResult = {
356
+ type: 'tool_result',
357
+ tool_use_id: msg.tool_call_id ?? '',
358
+ content: msg.content ?? '',
359
+ };
360
+ if (last?.role === 'user' && Array.isArray(last.content)) {
361
+ last.content.push(toolResult);
362
+ }
363
+ else {
364
+ anthropicMessages.push({ role: 'user', content: [toolResult] });
365
+ }
366
+ continue;
367
+ }
368
+ anthropicMessages.push({ role: 'user', content: msg.content ?? '' });
369
+ }
370
+ const anthropicTools = tools.map(t => ({
371
+ name: t.function.name,
372
+ description: t.function.description,
373
+ input_schema: t.function.parameters,
374
+ }));
375
+ const body = {
376
+ model: provider.model,
377
+ max_tokens: 4096,
378
+ messages: anthropicMessages,
379
+ };
380
+ if (system)
381
+ body.system = system;
382
+ if (anthropicTools.length > 0)
383
+ body.tools = anthropicTools;
384
+ if (temperature !== undefined)
385
+ body.temperature = temperature;
386
+ const res = await fetch(`${provider.baseUrl}/v1/messages`, {
387
+ method: 'POST',
388
+ headers: {
389
+ 'Content-Type': 'application/json',
390
+ 'x-api-key': provider.apiKey ?? '',
391
+ 'anthropic-version': '2023-06-01',
392
+ },
393
+ body: JSON.stringify(body),
394
+ });
395
+ if (!res.ok) {
396
+ const text = await res.text();
397
+ throw new Error(`Anthropic error (${res.status}): ${text}`);
398
+ }
399
+ const data = await res.json();
400
+ const message = { role: 'assistant', content: null };
401
+ const textParts = [];
402
+ const toolCalls = [];
403
+ for (const block of data.content) {
404
+ if (block.type === 'text') {
405
+ textParts.push(block.text);
406
+ }
407
+ else if (block.type === 'tool_use') {
408
+ toolCalls.push({
409
+ id: block.id,
410
+ type: 'function',
411
+ function: {
412
+ name: block.name,
413
+ arguments: block.input,
414
+ },
415
+ });
416
+ }
417
+ }
418
+ if (textParts.length > 0)
419
+ message.content = textParts.join('\n');
420
+ if (toolCalls.length > 0)
421
+ message.tool_calls = toolCalls;
422
+ const inputTokens = data.usage?.input_tokens ?? 0;
423
+ const outputTokens = data.usage?.output_tokens ?? 0;
424
+ const usage = {
425
+ promptTokens: inputTokens,
426
+ completionTokens: outputTokens,
427
+ totalTokens: inputTokens + outputTokens,
428
+ };
429
+ return { message, usage };
430
+ }
431
+ // ─── SDK-based completions (Cerebras, Groq, Anthropic) ──────────────
432
+ async function cerebrasSdkCompletion(provider, messages, tools, temperature) {
433
+ const client = await getCerebrasClient(provider.apiKey);
434
+ const body = {
435
+ model: provider.model,
436
+ messages: messages.map(m => {
437
+ const msg = { role: m.role };
438
+ if (m.content !== undefined)
439
+ msg.content = m.content;
440
+ if (m.tool_calls)
441
+ msg.tool_calls = m.tool_calls.map(tc => ({
442
+ id: tc.id, type: 'function',
443
+ function: { name: tc.function.name, arguments: typeof tc.function.arguments === 'string' ? tc.function.arguments : JSON.stringify(tc.function.arguments) },
444
+ }));
445
+ if (m.tool_call_id)
446
+ msg.tool_call_id = m.tool_call_id;
447
+ return msg;
448
+ }),
449
+ };
450
+ if (tools.length > 0) {
451
+ body.tools = tools;
452
+ body.parallel_tool_calls = true;
453
+ }
454
+ if (temperature !== undefined)
455
+ body.temperature = temperature;
456
+ const response = await client.chat.completions.create(body);
457
+ const choice = response.choices?.[0]?.message;
458
+ if (!choice)
459
+ throw new Error('Cerebras SDK returned no choices');
460
+ let content = choice.content ?? null;
461
+ if (content)
462
+ content = content.replace(/<think>[\s\S]*?<\/think>/g, '').trim() || null;
463
+ const message = { role: 'assistant', content };
464
+ if (choice.tool_calls && choice.tool_calls.length > 0) {
465
+ message.tool_calls = choice.tool_calls.map((tc) => ({
466
+ id: tc.id, type: 'function',
467
+ function: { name: tc.function.name, arguments: parseArguments(tc.function.arguments) },
468
+ }));
469
+ }
470
+ else if (message.content && tools.length > 0) {
471
+ const extracted = extractToolCallsFromText(message.content, tools);
472
+ if (extracted.length > 0) {
473
+ message.tool_calls = extracted;
474
+ message.content = null;
475
+ }
476
+ }
477
+ return {
478
+ message,
479
+ usage: {
480
+ promptTokens: response.usage?.prompt_tokens ?? 0,
481
+ completionTokens: response.usage?.completion_tokens ?? 0,
482
+ totalTokens: response.usage?.total_tokens ?? 0,
483
+ },
484
+ };
485
+ }
486
+ async function groqSdkCompletion(provider, messages, tools, temperature) {
487
+ const client = await getGroqClient(provider.apiKey);
488
+ const body = {
489
+ model: provider.model,
490
+ messages: messages.map(m => {
491
+ const msg = { role: m.role };
492
+ if (m.content !== undefined)
493
+ msg.content = m.content;
494
+ if (m.tool_calls)
495
+ msg.tool_calls = m.tool_calls.map(tc => ({
496
+ id: tc.id, type: 'function',
497
+ function: { name: tc.function.name, arguments: typeof tc.function.arguments === 'string' ? tc.function.arguments : JSON.stringify(tc.function.arguments) },
498
+ }));
499
+ if (m.tool_call_id)
500
+ msg.tool_call_id = m.tool_call_id;
501
+ return msg;
502
+ }),
503
+ };
504
+ if (tools.length > 0) {
505
+ body.tools = tools;
506
+ body.parallel_tool_calls = true;
507
+ }
508
+ if (temperature !== undefined)
509
+ body.temperature = temperature;
510
+ const response = await client.chat.completions.create(body);
511
+ const choice = response.choices?.[0]?.message;
512
+ if (!choice)
513
+ throw new Error('Groq SDK returned no choices');
514
+ let content = choice.content ?? null;
515
+ if (content)
516
+ content = content.replace(/<think>[\s\S]*?<\/think>/g, '').trim() || null;
517
+ const message = { role: 'assistant', content };
518
+ if (choice.tool_calls && choice.tool_calls.length > 0) {
519
+ message.tool_calls = choice.tool_calls.map((tc) => ({
520
+ id: tc.id, type: 'function',
521
+ function: { name: tc.function.name, arguments: parseArguments(tc.function.arguments) },
522
+ }));
523
+ }
524
+ else if (message.content && tools.length > 0) {
525
+ const extracted = extractToolCallsFromText(message.content, tools);
526
+ if (extracted.length > 0) {
527
+ message.tool_calls = extracted;
528
+ message.content = null;
529
+ }
530
+ }
531
+ return {
532
+ message,
533
+ usage: {
534
+ promptTokens: response.usage?.prompt_tokens ?? 0,
535
+ completionTokens: response.usage?.completion_tokens ?? 0,
536
+ totalTokens: response.usage?.total_tokens ?? 0,
537
+ },
538
+ };
539
+ }
540
+ async function anthropicSdkCompletion(provider, messages, tools, temperature) {
541
+ const client = await getAnthropicClient(provider.apiKey);
542
+ // Extract system message
543
+ let system;
544
+ const anthropicMessages = [];
545
+ for (const msg of messages) {
546
+ if (msg.role === 'system') {
547
+ system = msg.content ?? undefined;
548
+ continue;
549
+ }
550
+ if (msg.role === 'assistant') {
551
+ const content = [];
552
+ if (msg.content)
553
+ content.push({ type: 'text', text: msg.content });
554
+ if (msg.tool_calls) {
555
+ for (const tc of msg.tool_calls) {
556
+ content.push({
557
+ type: 'tool_use', id: tc.id ?? `call_${Date.now()}`, name: tc.function.name,
558
+ input: typeof tc.function.arguments === 'string' ? parseArguments(tc.function.arguments) : tc.function.arguments,
559
+ });
560
+ }
561
+ }
562
+ anthropicMessages.push({ role: 'assistant', content });
563
+ continue;
564
+ }
565
+ if (msg.role === 'tool') {
566
+ const toolResult = { type: 'tool_result', tool_use_id: msg.tool_call_id ?? '', content: msg.content ?? '' };
567
+ const last = anthropicMessages[anthropicMessages.length - 1];
568
+ if (last?.role === 'user' && Array.isArray(last.content)) {
569
+ last.content.push(toolResult);
570
+ }
571
+ else {
572
+ anthropicMessages.push({ role: 'user', content: [toolResult] });
573
+ }
574
+ continue;
575
+ }
576
+ anthropicMessages.push({ role: 'user', content: msg.content ?? '' });
577
+ }
578
+ const anthropicTools = tools.map(t => ({
579
+ name: t.function.name,
580
+ description: t.function.description,
581
+ input_schema: t.function.parameters,
582
+ }));
583
+ const params = {
584
+ model: provider.model,
585
+ max_tokens: 4096,
586
+ messages: anthropicMessages,
587
+ };
588
+ if (system)
589
+ params.system = system;
590
+ if (anthropicTools.length > 0)
591
+ params.tools = anthropicTools;
592
+ if (temperature !== undefined)
593
+ params.temperature = temperature;
594
+ const response = await client.messages.create(params);
595
+ const chatMsg = { role: 'assistant', content: null };
596
+ const textParts = [];
597
+ const toolCalls = [];
598
+ for (const block of response.content) {
599
+ if (block.type === 'text') {
600
+ textParts.push(block.text);
601
+ }
602
+ else if (block.type === 'tool_use') {
603
+ toolCalls.push({
604
+ id: block.id, type: 'function',
605
+ function: { name: block.name, arguments: block.input },
606
+ });
607
+ }
608
+ }
609
+ if (textParts.length > 0)
610
+ chatMsg.content = textParts.join('\n');
611
+ if (toolCalls.length > 0)
612
+ chatMsg.tool_calls = toolCalls;
613
+ return {
614
+ message: chatMsg,
615
+ usage: {
616
+ promptTokens: response.usage?.input_tokens ?? 0,
617
+ completionTokens: response.usage?.output_tokens ?? 0,
618
+ totalTokens: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0),
619
+ },
620
+ };
621
+ }
622
+ // ─── Utilities ──────────────────────────────────────────────────────
623
+ function parseArguments(args) {
624
+ try {
625
+ return JSON.parse(args);
626
+ }
627
+ catch {
628
+ return {};
629
+ }
630
+ }
631
+ export function getToolCallArgs(tc) {
632
+ if (typeof tc.function.arguments === 'string') {
633
+ return parseArguments(tc.function.arguments);
634
+ }
635
+ return tc.function.arguments;
636
+ }
637
+ /**
638
+ * Extract tool calls from text content when a model doesn't use native tool calling.
639
+ */
640
+ function extractToolCallsFromText(text, tools) {
641
+ const toolNames = new Set(tools.map(t => t.function.name));
642
+ const results = [];
643
+ const jsonCandidates = extractJsonBlocks(text);
644
+ for (const candidate of jsonCandidates) {
645
+ try {
646
+ const parsed = JSON.parse(candidate);
647
+ const items = Array.isArray(parsed) ? parsed : [parsed];
648
+ for (const item of items) {
649
+ if (!item || typeof item !== 'object')
650
+ continue;
651
+ if (typeof item.name === 'string' && toolNames.has(item.name)) {
652
+ results.push({
653
+ id: `text_${Date.now()}_${results.length}`,
654
+ type: 'function',
655
+ function: {
656
+ name: item.name,
657
+ arguments: item.arguments ?? item.parameters ?? {},
658
+ },
659
+ });
660
+ }
661
+ }
662
+ }
663
+ catch { /* not valid JSON */ }
664
+ }
665
+ return results;
666
+ }
667
+ /** Extract potential JSON blocks from text (fenced or bare). */
668
+ function extractJsonBlocks(text) {
669
+ const blocks = [];
670
+ // Fenced code blocks: ```json ... ``` or ``` ... ```
671
+ const fenced = text.matchAll(/```(?:json)?\s*\n?([\s\S]*?)```/g);
672
+ for (const m of fenced) {
673
+ blocks.push(m[1].trim());
674
+ }
675
+ // Bare JSON: find outermost { } or [ ] blocks
676
+ for (let i = 0; i < text.length; i++) {
677
+ if (text[i] === '{' || text[i] === '[') {
678
+ const close = text[i] === '{' ? '}' : ']';
679
+ let depth = 1;
680
+ let j = i + 1;
681
+ while (j < text.length && depth > 0) {
682
+ if (text[j] === text[i])
683
+ depth++;
684
+ else if (text[j] === close)
685
+ depth--;
686
+ j++;
687
+ }
688
+ if (depth === 0) {
689
+ blocks.push(text.slice(i, j));
690
+ i = j - 1;
691
+ }
692
+ }
693
+ }
694
+ return blocks;
695
+ }
696
+ //# sourceMappingURL=providers.js.map