@jxburros/ai-nugget 0.4.1 → 0.6.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 (175) hide show
  1. package/README.md +132 -385
  2. package/UPGRADING.md +158 -0
  3. package/dist/adapters/engines/anthropic.d.ts +7 -1
  4. package/dist/adapters/engines/anthropic.js +64 -37
  5. package/dist/adapters/engines/base.d.ts +29 -2
  6. package/dist/adapters/engines/base.js +52 -8
  7. package/dist/adapters/engines/google.d.ts +7 -1
  8. package/dist/adapters/engines/google.js +73 -29
  9. package/dist/adapters/engines/ollama.d.ts +2 -1
  10. package/dist/adapters/engines/ollama.js +51 -15
  11. package/dist/adapters/engines/openaiChat.d.ts +2 -1
  12. package/dist/adapters/engines/openaiChat.js +60 -30
  13. package/dist/adapters/profiles.d.ts +21 -1
  14. package/dist/adapters/profiles.js +12 -2
  15. package/dist/agent/loop.d.ts +43 -1
  16. package/dist/agent/loop.js +116 -12
  17. package/dist/cjs/adapters/engines/anthropic.js +308 -0
  18. package/dist/cjs/adapters/engines/base.js +147 -0
  19. package/dist/cjs/adapters/engines/google.js +284 -0
  20. package/dist/cjs/adapters/engines/ollama.js +199 -0
  21. package/dist/cjs/adapters/engines/openaiChat.js +261 -0
  22. package/dist/cjs/adapters/index.js +22 -0
  23. package/dist/cjs/adapters/profiles.js +125 -0
  24. package/dist/cjs/agent/index.js +18 -0
  25. package/dist/cjs/agent/loop.js +372 -0
  26. package/dist/cjs/agent/tools.js +51 -0
  27. package/dist/cjs/connect.js +27 -0
  28. package/dist/cjs/errors.js +110 -0
  29. package/dist/cjs/handler.js +561 -0
  30. package/dist/cjs/index.js +27 -0
  31. package/dist/cjs/json.js +121 -0
  32. package/dist/cjs/keys.js +81 -0
  33. package/dist/cjs/package.json +3 -0
  34. package/dist/cjs/policy.js +42 -0
  35. package/dist/cjs/redact.js +78 -0
  36. package/dist/cjs/tokens.js +22 -0
  37. package/dist/cjs/transport.js +172 -0
  38. package/dist/cjs/types.js +6 -0
  39. package/dist/cjs/util.js +112 -0
  40. package/dist/errors.js +25 -1
  41. package/dist/handler.d.ts +61 -1
  42. package/dist/handler.js +239 -59
  43. package/dist/redact.d.ts +18 -0
  44. package/dist/redact.js +29 -1
  45. package/dist/transport.d.ts +3 -1
  46. package/dist/transport.js +21 -1
  47. package/dist/types.d.ts +96 -2
  48. package/dist/util.d.ts +15 -0
  49. package/dist/util.js +46 -0
  50. package/nugget/VERSION.txt +2 -2
  51. package/nugget/dist/adapters/engines/anthropic.d.ts +14 -0
  52. package/nugget/dist/adapters/engines/anthropic.d.ts.map +1 -0
  53. package/nugget/dist/adapters/engines/anthropic.js +305 -0
  54. package/nugget/dist/adapters/engines/anthropic.js.map +1 -0
  55. package/nugget/dist/adapters/engines/base.d.ts +50 -0
  56. package/nugget/dist/adapters/engines/base.d.ts.map +1 -0
  57. package/nugget/dist/adapters/engines/base.js +137 -0
  58. package/nugget/dist/adapters/engines/base.js.map +1 -0
  59. package/nugget/dist/adapters/engines/google.d.ts +24 -0
  60. package/nugget/dist/adapters/engines/google.d.ts.map +1 -0
  61. package/nugget/dist/adapters/engines/google.js +281 -0
  62. package/nugget/dist/adapters/engines/google.js.map +1 -0
  63. package/nugget/dist/adapters/engines/ollama.d.ts +11 -0
  64. package/nugget/dist/adapters/engines/ollama.d.ts.map +1 -0
  65. package/nugget/dist/adapters/engines/ollama.js +196 -0
  66. package/nugget/dist/adapters/engines/ollama.js.map +1 -0
  67. package/nugget/dist/adapters/engines/openaiChat.d.ts +16 -0
  68. package/nugget/dist/adapters/engines/openaiChat.d.ts.map +1 -0
  69. package/nugget/dist/adapters/engines/openaiChat.js +258 -0
  70. package/nugget/dist/adapters/engines/openaiChat.js.map +1 -0
  71. package/nugget/dist/adapters/index.d.ts +5 -0
  72. package/nugget/dist/adapters/index.d.ts.map +1 -0
  73. package/nugget/dist/adapters/index.js +17 -0
  74. package/nugget/dist/adapters/index.js.map +1 -0
  75. package/nugget/dist/adapters/profiles.d.ts +62 -0
  76. package/nugget/dist/adapters/profiles.d.ts.map +1 -0
  77. package/nugget/dist/adapters/profiles.js +121 -0
  78. package/nugget/dist/adapters/profiles.js.map +1 -0
  79. package/nugget/dist/agent/index.d.ts +3 -0
  80. package/nugget/dist/agent/index.d.ts.map +1 -0
  81. package/nugget/dist/agent/index.js +3 -0
  82. package/nugget/dist/agent/index.js.map +1 -0
  83. package/nugget/dist/agent/loop.d.ts +112 -0
  84. package/nugget/dist/agent/loop.d.ts.map +1 -0
  85. package/nugget/dist/agent/loop.js +370 -0
  86. package/nugget/dist/agent/loop.js.map +1 -0
  87. package/nugget/dist/agent/tools.d.ts +29 -0
  88. package/nugget/dist/agent/tools.d.ts.map +1 -0
  89. package/nugget/dist/agent/tools.js +48 -0
  90. package/nugget/dist/agent/tools.js.map +1 -0
  91. package/nugget/dist/cjs/adapters/engines/anthropic.js +308 -0
  92. package/nugget/dist/cjs/adapters/engines/base.js +147 -0
  93. package/nugget/dist/cjs/adapters/engines/google.js +284 -0
  94. package/nugget/dist/cjs/adapters/engines/ollama.js +199 -0
  95. package/nugget/dist/cjs/adapters/engines/openaiChat.js +261 -0
  96. package/nugget/dist/cjs/adapters/index.js +22 -0
  97. package/nugget/dist/cjs/adapters/profiles.js +125 -0
  98. package/nugget/dist/cjs/agent/index.js +18 -0
  99. package/nugget/dist/cjs/agent/loop.js +372 -0
  100. package/nugget/dist/cjs/agent/tools.js +51 -0
  101. package/nugget/dist/cjs/connect.js +27 -0
  102. package/nugget/dist/cjs/errors.js +110 -0
  103. package/nugget/dist/cjs/handler.js +561 -0
  104. package/nugget/dist/cjs/index.js +27 -0
  105. package/nugget/dist/cjs/json.js +121 -0
  106. package/nugget/dist/cjs/keys.js +81 -0
  107. package/nugget/dist/cjs/package.json +3 -0
  108. package/nugget/dist/cjs/policy.js +42 -0
  109. package/nugget/dist/cjs/redact.js +78 -0
  110. package/nugget/dist/cjs/tokens.js +22 -0
  111. package/nugget/dist/cjs/transport.js +172 -0
  112. package/nugget/dist/cjs/types.js +6 -0
  113. package/nugget/dist/cjs/util.js +112 -0
  114. package/nugget/dist/connect.d.ts +31 -0
  115. package/nugget/dist/connect.d.ts.map +1 -0
  116. package/nugget/dist/connect.js +25 -0
  117. package/nugget/dist/connect.js.map +1 -0
  118. package/nugget/dist/errors.d.ts +22 -0
  119. package/nugget/dist/errors.d.ts.map +1 -0
  120. package/nugget/dist/errors.js +104 -0
  121. package/nugget/dist/errors.js.map +1 -0
  122. package/nugget/dist/handler.d.ts +108 -0
  123. package/nugget/dist/handler.d.ts.map +1 -0
  124. package/nugget/dist/handler.js +557 -0
  125. package/nugget/dist/handler.js.map +1 -0
  126. package/nugget/dist/index.d.ts +12 -0
  127. package/nugget/dist/index.d.ts.map +1 -0
  128. package/nugget/dist/index.js +12 -0
  129. package/nugget/dist/index.js.map +1 -0
  130. package/nugget/dist/json.d.ts +7 -0
  131. package/nugget/dist/json.d.ts.map +1 -0
  132. package/nugget/dist/json.js +114 -0
  133. package/nugget/dist/json.js.map +1 -0
  134. package/nugget/dist/keys.d.ts +7 -0
  135. package/nugget/dist/keys.d.ts.map +1 -0
  136. package/nugget/dist/keys.js +75 -0
  137. package/nugget/dist/keys.js.map +1 -0
  138. package/nugget/dist/policy.d.ts +6 -0
  139. package/nugget/dist/policy.d.ts.map +1 -0
  140. package/nugget/dist/policy.js +37 -0
  141. package/nugget/dist/policy.js.map +1 -0
  142. package/nugget/dist/redact.d.ts +26 -0
  143. package/nugget/dist/redact.d.ts.map +1 -0
  144. package/nugget/dist/redact.js +74 -0
  145. package/nugget/dist/redact.js.map +1 -0
  146. package/nugget/dist/tokens.d.ts +5 -0
  147. package/nugget/dist/tokens.d.ts.map +1 -0
  148. package/nugget/dist/tokens.js +18 -0
  149. package/nugget/dist/tokens.js.map +1 -0
  150. package/nugget/dist/transport.d.ts +30 -0
  151. package/nugget/dist/transport.d.ts.map +1 -0
  152. package/nugget/dist/transport.js +165 -0
  153. package/nugget/dist/transport.js.map +1 -0
  154. package/nugget/dist/types.d.ts +285 -0
  155. package/nugget/dist/types.d.ts.map +1 -0
  156. package/nugget/dist/types.js +4 -0
  157. package/nugget/dist/types.js.map +1 -0
  158. package/nugget/dist/util.d.ts +24 -0
  159. package/nugget/dist/util.d.ts.map +1 -0
  160. package/nugget/dist/util.js +101 -0
  161. package/nugget/dist/util.js.map +1 -0
  162. package/nugget/src/adapters/engines/anthropic.ts +62 -37
  163. package/nugget/src/adapters/engines/base.ts +50 -7
  164. package/nugget/src/adapters/engines/google.ts +72 -29
  165. package/nugget/src/adapters/engines/ollama.ts +51 -17
  166. package/nugget/src/adapters/engines/openaiChat.ts +61 -32
  167. package/nugget/src/adapters/profiles.ts +53 -3
  168. package/nugget/src/agent/loop.ts +153 -13
  169. package/nugget/src/errors.ts +25 -1
  170. package/nugget/src/handler.ts +279 -57
  171. package/nugget/src/redact.ts +32 -1
  172. package/nugget/src/transport.ts +20 -1
  173. package/nugget/src/types.ts +96 -1
  174. package/nugget/src/util.ts +50 -0
  175. package/package.json +18 -5
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenAIChatAdapter = void 0;
4
+ const errors_js_1 = require("../../errors.js");
5
+ const tokens_js_1 = require("../../tokens.js");
6
+ const transport_js_1 = require("../../transport.js");
7
+ const util_js_1 = require("../../util.js");
8
+ const base_js_1 = require("./base.js");
9
+ class OpenAIChatAdapter {
10
+ profile;
11
+ provider;
12
+ constructor(provider, profile) {
13
+ this.profile = profile;
14
+ this.provider = provider;
15
+ }
16
+ async chat(conn, req) {
17
+ let final;
18
+ for await (const event of this.stream(conn, req)) {
19
+ if (event.type === 'done')
20
+ final = event.result;
21
+ if (event.type === 'error')
22
+ throw event.error;
23
+ }
24
+ if (!final)
25
+ throw new errors_js_1.AIError('Provider stream ended without a result', { kind: 'invalid_response', provider: conn.provider });
26
+ return final;
27
+ }
28
+ async *stream(conn, req) {
29
+ const started = Date.now();
30
+ let firstTokenMs = null;
31
+ let text = '';
32
+ let toolCalls = [];
33
+ let inputTokens;
34
+ let outputTokens;
35
+ let finish;
36
+ let sawTerminal = false;
37
+ const timeout = (0, base_js_1.streamTimeout)(conn, req.signal);
38
+ yield { type: 'start', callId: '', provider: conn.provider, model: req.model };
39
+ try {
40
+ const res = await (0, transport_js_1.postResponse)(urlFor(conn, this.profile, req), openAiBody(req, this.profile), conn.headers, timeout.signal, conn.provider);
41
+ const contentType = res.headers.get('content-type') ?? '';
42
+ if (!contentType.includes('text/event-stream')) {
43
+ // Server ignored stream:true (or is a buffered gateway) — recover the whole body.
44
+ const rawText = await res.text().catch(() => '');
45
+ const raw = (0, base_js_1.safeParse)(rawText) ?? { text: rawText };
46
+ const parsed = parseOpenAiResponse(raw);
47
+ text = parsed.text;
48
+ toolCalls = parsed.toolCalls;
49
+ inputTokens = parsed.inputTokens;
50
+ outputTokens = parsed.outputTokens;
51
+ finish = parsed.finish;
52
+ sawTerminal = true;
53
+ if (parsed.reasoning)
54
+ yield { type: 'reasoning', text: parsed.reasoning };
55
+ if (text)
56
+ yield { type: 'delta', text };
57
+ for (const call of toolCalls)
58
+ yield { type: 'tool_call', call };
59
+ }
60
+ else {
61
+ const partialTools = new Map();
62
+ for await (const line of (0, transport_js_1.sseLines)(res)) {
63
+ timeout.bump();
64
+ const chunk = (0, base_js_1.safeParse)(line);
65
+ const record = (0, util_js_1.asRecord)(chunk);
66
+ if (!record)
67
+ continue;
68
+ const usage = (0, util_js_1.asRecord)(record.usage);
69
+ inputTokens = (0, util_js_1.asNumber)(usage?.prompt_tokens) ?? inputTokens;
70
+ outputTokens = (0, util_js_1.asNumber)(usage?.completion_tokens) ?? outputTokens;
71
+ const choices = Array.isArray(record.choices) ? record.choices : [];
72
+ const choice = (0, util_js_1.asRecord)(choices[0]);
73
+ finish = (0, util_js_1.asString)(choice?.finish_reason) ?? finish;
74
+ if (choice?.finish_reason)
75
+ sawTerminal = true;
76
+ const delta = (0, util_js_1.asRecord)(choice?.delta);
77
+ // Reasoning tokens (DeepSeek `reasoning_content`, others `reasoning`)
78
+ // ride their own channel so they don't contaminate the answer text.
79
+ const reasoning = (0, util_js_1.asString)(delta?.reasoning_content) ?? (0, util_js_1.asString)(delta?.reasoning);
80
+ if (reasoning)
81
+ yield { type: 'reasoning', text: reasoning };
82
+ const piece = (0, util_js_1.asString)(delta?.content);
83
+ if (piece) {
84
+ if (firstTokenMs === null)
85
+ firstTokenMs = Date.now() - started;
86
+ text += piece;
87
+ yield { type: 'delta', text: piece };
88
+ }
89
+ const calls = Array.isArray(delta?.tool_calls) ? delta.tool_calls : [];
90
+ for (const callValue of calls) {
91
+ const call = (0, util_js_1.asRecord)(callValue);
92
+ const index = (0, util_js_1.asNumber)(call?.index) ?? 0;
93
+ const current = partialTools.get(index) ?? { raw: '' };
94
+ current.id = (0, util_js_1.asString)(call?.id) ?? current.id;
95
+ const fn = (0, util_js_1.asRecord)(call?.function);
96
+ current.name = (0, util_js_1.asString)(fn?.name) ?? current.name;
97
+ current.raw += (0, util_js_1.asString)(fn?.arguments) ?? '';
98
+ partialTools.set(index, current);
99
+ }
100
+ }
101
+ toolCalls = [...partialTools.values()].filter((call) => call.name).map((call) => ({
102
+ id: call.id ?? (0, base_js_1.randomId)(),
103
+ name: call.name,
104
+ raw: call.raw,
105
+ arguments: (0, base_js_1.parseArgs)(call.raw),
106
+ }));
107
+ for (const call of toolCalls)
108
+ yield { type: 'tool_call', call };
109
+ }
110
+ if (!sawTerminal)
111
+ yield (0, base_js_1.streamAnomaly)('stream ended without a finish_reason');
112
+ const result = makeResult(conn, req, text, toolCalls, finish, started, firstTokenMs, inputTokens, outputTokens);
113
+ yield { type: 'done', result };
114
+ }
115
+ catch (error) {
116
+ throw (0, base_js_1.streamError)(error, timeout, conn.provider);
117
+ }
118
+ finally {
119
+ timeout.done();
120
+ }
121
+ }
122
+ listModels(conn) {
123
+ return (0, base_js_1.listOpenModels)(conn, this.profile);
124
+ }
125
+ health(conn) {
126
+ return (0, base_js_1.health)(conn, this.profile);
127
+ }
128
+ async embed(conn, req) {
129
+ const inputs = Array.isArray(req.input) ? req.input : [req.input];
130
+ const { data } = await (0, transport_js_1.fetchJson)((0, util_js_1.joinUrl)(conn.baseUrl, '/embeddings'), {
131
+ method: 'POST',
132
+ headers: { 'content-type': 'application/json', ...conn.headers },
133
+ body: JSON.stringify((0, util_js_1.applyProviderOptions)({ model: req.model, input: inputs }, req.providerOptions)),
134
+ timeoutMs: conn.timeoutMs ?? base_js_1.DEFAULT_TIMEOUT_MS,
135
+ provider: conn.provider,
136
+ });
137
+ const record = (0, util_js_1.asRecord)(data);
138
+ const rows = Array.isArray(record?.data) ? record.data : [];
139
+ // Order by the `index` each row reports, falling back to array order.
140
+ const byIndex = rows
141
+ .map((row, i) => ({ index: (0, util_js_1.asNumber)((0, util_js_1.asRecord)(row)?.index) ?? i, embedding: asEmbedding((0, util_js_1.asRecord)(row)?.embedding) }))
142
+ .sort((a, b) => a.index - b.index);
143
+ const usage = (0, util_js_1.asRecord)(record?.usage);
144
+ const inputTokens = (0, util_js_1.asNumber)(usage?.prompt_tokens);
145
+ return {
146
+ embeddings: byIndex.map((row) => row.embedding),
147
+ model: req.model,
148
+ usage: { inputTokens, outputTokens: 0, estimated: inputTokens === undefined },
149
+ source: { provider: conn.provider, connectionId: conn.id, baseUrl: conn.baseUrl },
150
+ raw: data,
151
+ };
152
+ }
153
+ }
154
+ exports.OpenAIChatAdapter = OpenAIChatAdapter;
155
+ function asEmbedding(value) {
156
+ return Array.isArray(value) ? value.filter((n) => typeof n === 'number') : [];
157
+ }
158
+ function openAiBody(req, profile) {
159
+ const body = {
160
+ model: req.model,
161
+ messages: req.messages.map(toOpenAiMessage),
162
+ temperature: req.temperature,
163
+ top_p: req.topP,
164
+ stop: req.stopSequences,
165
+ response_format: responseFormatFor(req, profile),
166
+ tools: req.tools?.map((tool) => ({ type: 'function', function: tool })),
167
+ tool_choice: typeof req.toolChoice === 'object' ? { type: 'function', function: { name: req.toolChoice.name } } : req.toolChoice,
168
+ stream: true,
169
+ // Many OpenAI-compatible local servers (llama.cpp, LM Studio, vLLM) choke
170
+ // on or ignore stream_options — only send it where the profile confirms
171
+ // the server understands it.
172
+ stream_options: profile.quirks?.supportsUsageInStream ? { include_usage: true } : undefined,
173
+ };
174
+ if (req.maxTokens !== undefined)
175
+ body[profile.quirks?.maxTokensParam ?? 'max_tokens'] = req.maxTokens;
176
+ // providerOptions carries OpenAI-native fields the nugget doesn't model
177
+ // (`reasoning_effort`, `parallel_tool_calls`, `seed`, `logprobs`, …). `apiVersion`
178
+ // is consumed by urlFor for Azure and stripped here so it never hits the body.
179
+ const { apiVersion, ...passthrough } = req.providerOptions ?? {};
180
+ void apiVersion;
181
+ return (0, util_js_1.applyProviderOptions)(body, Object.keys(passthrough).length ? passthrough : undefined);
182
+ }
183
+ function responseFormatFor(req, profile) {
184
+ if (req.responseFormat?.type !== 'json')
185
+ return undefined;
186
+ if (req.responseFormat.schema && profile.quirks?.supportsJsonSchema) {
187
+ return { type: 'json_schema', json_schema: { name: 'response', schema: req.responseFormat.schema } };
188
+ }
189
+ return { type: 'json_object' };
190
+ }
191
+ function toOpenAiMessage(message) {
192
+ return {
193
+ role: message.role,
194
+ content: typeof message.content === 'string'
195
+ ? message.content
196
+ : message.content.map((part) => part.type === 'image'
197
+ ? { type: 'image_url', image_url: { url: `data:${part.mimeType ?? 'image/png'};base64,${part.imageBase64 ?? ''}` } }
198
+ : { type: 'text', text: part.text ?? '' }),
199
+ name: message.role === 'tool' ? message.name : undefined,
200
+ tool_call_id: message.toolCallId,
201
+ tool_calls: message.toolCalls?.map((call) => ({ id: call.id, type: 'function', function: { name: call.name, arguments: call.raw ?? JSON.stringify(call.arguments) } })),
202
+ };
203
+ }
204
+ function urlFor(conn, profile, req) {
205
+ if (profile.quirks?.urlTemplate) {
206
+ // `{apiVersion}` (Azure) is filled from a per-call override, then the
207
+ // profile default — so a retired Azure api-version is fixable without a
208
+ // library release: pass `providerOptions: { apiVersion: '2025-01-01-preview' }`.
209
+ const apiVersion = (0, util_js_1.asString)(req.providerOptions?.apiVersion) ?? profile.quirks.apiVersion ?? '';
210
+ return profile.quirks.urlTemplate
211
+ .replace('{baseUrl}', conn.baseUrl)
212
+ .replace('{model}', encodeURIComponent(req.model))
213
+ .replace('{apiVersion}', apiVersion);
214
+ }
215
+ return (0, util_js_1.joinUrl)(conn.baseUrl, '/chat/completions');
216
+ }
217
+ function parseOpenAiResponse(raw) {
218
+ const record = (0, util_js_1.asRecord)(raw);
219
+ const usage = (0, util_js_1.asRecord)(record?.usage);
220
+ const choice = (0, util_js_1.asRecord)(Array.isArray(record?.choices) ? record.choices[0] : undefined);
221
+ const message = (0, util_js_1.asRecord)(choice?.message);
222
+ const text = (0, util_js_1.asString)(message?.content) ?? (0, util_js_1.asString)(record?.text) ?? '';
223
+ const reasoning = (0, util_js_1.asString)(message?.reasoning_content) ?? (0, util_js_1.asString)(message?.reasoning);
224
+ const calls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
225
+ return {
226
+ text,
227
+ reasoning,
228
+ finish: (0, util_js_1.asString)(choice?.finish_reason),
229
+ toolCalls: calls.map((value) => {
230
+ const call = (0, util_js_1.asRecord)(value);
231
+ const fn = (0, util_js_1.asRecord)(call?.function);
232
+ const rawArgs = (0, util_js_1.asString)(fn?.arguments) ?? '{}';
233
+ return { id: (0, util_js_1.asString)(call?.id) ?? (0, base_js_1.randomId)(), name: (0, util_js_1.asString)(fn?.name) ?? 'unknown', raw: rawArgs, arguments: (0, base_js_1.parseArgs)(rawArgs) };
234
+ }),
235
+ inputTokens: (0, util_js_1.asNumber)(usage?.prompt_tokens),
236
+ outputTokens: (0, util_js_1.asNumber)(usage?.completion_tokens),
237
+ };
238
+ }
239
+ function makeResult(conn, req, text, toolCalls, finish, started, firstTokenMs, inputTokens, outputTokens) {
240
+ const usage = inputTokens !== undefined || outputTokens !== undefined
241
+ ? { inputTokens, outputTokens, estimated: false }
242
+ : (0, tokens_js_1.estimatedUsage)((0, util_js_1.textFromMessages)(req.messages), text);
243
+ return {
244
+ text,
245
+ toolCalls: toolCalls.length ? toolCalls : undefined,
246
+ finishReason: mapFinish(finish, toolCalls.length > 0),
247
+ usage,
248
+ timing: { firstTokenMs, totalMs: Date.now() - started },
249
+ model: req.model,
250
+ source: { provider: conn.provider, connectionId: conn.id, baseUrl: conn.baseUrl },
251
+ };
252
+ }
253
+ function mapFinish(finish, hasToolCalls) {
254
+ if (hasToolCalls || finish === 'tool_calls' || finish === 'function_call')
255
+ return 'tool';
256
+ if (finish === 'length')
257
+ return 'length';
258
+ if (finish === 'content_filter')
259
+ return 'content_filter';
260
+ return 'stop';
261
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.profileFor = exports.PROVIDER_PROFILES = void 0;
4
+ exports.adapterFor = adapterFor;
5
+ const anthropic_js_1 = require("./engines/anthropic.js");
6
+ const google_js_1 = require("./engines/google.js");
7
+ const ollama_js_1 = require("./engines/ollama.js");
8
+ const openaiChat_js_1 = require("./engines/openaiChat.js");
9
+ const profiles_js_1 = require("./profiles.js");
10
+ function adapterFor(provider, baseUrl) {
11
+ const profile = (0, profiles_js_1.profileFor)(provider, baseUrl);
12
+ if (profile.engine === 'anthropic')
13
+ return new anthropic_js_1.AnthropicAdapter(provider);
14
+ if (profile.engine === 'google')
15
+ return new google_js_1.GoogleAdapter(provider);
16
+ if (profile.engine === 'ollama')
17
+ return new ollama_js_1.OllamaAdapter(provider);
18
+ return new openaiChat_js_1.OpenAIChatAdapter(provider, profile);
19
+ }
20
+ var profiles_js_2 = require("./profiles.js");
21
+ Object.defineProperty(exports, "PROVIDER_PROFILES", { enumerable: true, get: function () { return profiles_js_2.PROVIDER_PROFILES; } });
22
+ Object.defineProperty(exports, "profileFor", { enumerable: true, get: function () { return profiles_js_2.profileFor; } });
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROVIDER_PROFILES = void 0;
4
+ exports.profileFor = profileFor;
5
+ exports.applyAuth = applyAuth;
6
+ const HOSTED_CLOUD = { nativeTools: true, jsonMode: true, local: false, embeddable: false };
7
+ const LOCAL_RUNTIME = { nativeTools: false, jsonMode: false, local: true, embeddable: true };
8
+ exports.PROVIDER_PROFILES = {
9
+ openai: {
10
+ engine: 'openaiChat',
11
+ defaultBaseUrl: 'https://api.openai.com/v1',
12
+ auth: 'bearer',
13
+ listModelsPath: '/models',
14
+ capabilities: HOSTED_CLOUD,
15
+ quirks: { supportsUsageInStream: true, maxTokensParam: 'max_completion_tokens', supportsJsonSchema: true, supportsIdempotencyKey: true },
16
+ },
17
+ 'azure-openai': {
18
+ engine: 'openaiChat',
19
+ auth: 'api-key-header',
20
+ capabilities: HOSTED_CLOUD,
21
+ quirks: { urlTemplate: '{baseUrl}/openai/deployments/{model}/chat/completions?api-version={apiVersion}', apiVersion: '2024-10-21', maxTokensParam: 'max_completion_tokens', supportsJsonSchema: true },
22
+ },
23
+ openrouter: {
24
+ engine: 'openaiChat',
25
+ defaultBaseUrl: 'https://openrouter.ai/api/v1',
26
+ auth: 'bearer',
27
+ defaultHeaders: { 'HTTP-Referer': 'https://github.com/jxburros/AI-Nugget', 'X-Title': 'ai-nugget' },
28
+ listModelsPath: '/models',
29
+ capabilities: HOSTED_CLOUD,
30
+ quirks: { supportsUsageInStream: true },
31
+ },
32
+ groq: { engine: 'openaiChat', defaultBaseUrl: 'https://api.groq.com/openai/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
33
+ deepseek: { engine: 'openaiChat', defaultBaseUrl: 'https://api.deepseek.com/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
34
+ mistral: { engine: 'openaiChat', defaultBaseUrl: 'https://api.mistral.ai/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
35
+ together: { engine: 'openaiChat', defaultBaseUrl: 'https://api.together.xyz/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
36
+ fireworks: { engine: 'openaiChat', defaultBaseUrl: 'https://api.fireworks.ai/inference/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
37
+ cerebras: { engine: 'openaiChat', defaultBaseUrl: 'https://api.cerebras.ai/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
38
+ moonshot: { engine: 'openaiChat', defaultBaseUrl: 'https://api.moonshot.ai/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
39
+ // Cohere's OpenAI-compatible surface lives under /compatibility/v1.
40
+ cohere: { engine: 'openaiChat', defaultBaseUrl: 'https://api.cohere.ai/compatibility/v1', auth: 'bearer', listModelsPath: '/models', capabilities: HOSTED_CLOUD },
41
+ // Perplexity has no public model-listing endpoint, so listModels resolves empty.
42
+ perplexity: { engine: 'openaiChat', defaultBaseUrl: 'https://api.perplexity.ai', auth: 'bearer', capabilities: HOSTED_CLOUD },
43
+ lmstudio: {
44
+ engine: 'openaiChat',
45
+ defaultBaseUrl: 'http://localhost:1234/v1',
46
+ auth: 'none',
47
+ listModelsPath: '/models',
48
+ capabilities: LOCAL_RUNTIME,
49
+ quirks: { keyOptional: true },
50
+ },
51
+ llamacpp: {
52
+ engine: 'openaiChat',
53
+ defaultBaseUrl: 'http://localhost:8080/v1',
54
+ auth: 'none',
55
+ listModelsPath: '/models',
56
+ healthPath: '/health',
57
+ capabilities: LOCAL_RUNTIME,
58
+ quirks: { keyOptional: true, modelOptional: true },
59
+ },
60
+ vllm: {
61
+ engine: 'openaiChat',
62
+ defaultBaseUrl: 'http://localhost:8000/v1',
63
+ auth: 'none',
64
+ listModelsPath: '/models',
65
+ capabilities: LOCAL_RUNTIME,
66
+ quirks: { keyOptional: true },
67
+ },
68
+ ollama: {
69
+ engine: 'ollama',
70
+ defaultBaseUrl: 'http://localhost:11434',
71
+ auth: 'none',
72
+ listModelsPath: '/api/tags',
73
+ // Ollama's `format: 'json'` is engine-enforced (reliable regardless of
74
+ // model); its `tools` field is honored only by models trained for tool
75
+ // use, so nativeTools stays conservative like the other local runtimes.
76
+ capabilities: { ...LOCAL_RUNTIME, jsonMode: true },
77
+ quirks: { keyOptional: true },
78
+ },
79
+ anthropic: {
80
+ engine: 'anthropic',
81
+ defaultBaseUrl: 'https://api.anthropic.com',
82
+ auth: 'x-api-key',
83
+ defaultHeaders: { 'anthropic-version': '2023-06-01' },
84
+ capabilities: HOSTED_CLOUD,
85
+ quirks: { maxTokensRequired: true },
86
+ },
87
+ google: {
88
+ engine: 'google',
89
+ defaultBaseUrl: 'https://generativelanguage.googleapis.com',
90
+ auth: 'x-goog-api-key',
91
+ capabilities: HOSTED_CLOUD,
92
+ },
93
+ 'openai-compat': {
94
+ engine: 'openaiChat',
95
+ auth: 'bearer',
96
+ listModelsPath: '/models',
97
+ // Unknown endpoint — usually pointed at a local/self-hosted server, so
98
+ // assume the conservative local-runtime defaults rather than the hosted-cloud ones.
99
+ capabilities: LOCAL_RUNTIME,
100
+ // Most servers reached through this escape hatch (JX Runtime and other
101
+ // local OpenAI-compatible runtimes) don't require a key at all; keep
102
+ // `auth: 'bearer'` for the ones that do (a key is only sent if provided).
103
+ quirks: { keyOptional: true },
104
+ },
105
+ };
106
+ function profileFor(provider, baseUrl) {
107
+ return exports.PROVIDER_PROFILES[provider] ?? {
108
+ ...exports.PROVIDER_PROFILES['openai-compat'],
109
+ defaultBaseUrl: baseUrl,
110
+ };
111
+ }
112
+ function applyAuth(profile, apiKey, headers) {
113
+ const next = { ...profile.defaultHeaders, ...headers };
114
+ if (!apiKey)
115
+ return next;
116
+ if (profile.auth === 'bearer')
117
+ next.authorization = `Bearer ${apiKey}`;
118
+ else if (profile.auth === 'x-api-key')
119
+ next['x-api-key'] = apiKey;
120
+ else if (profile.auth === 'x-goog-api-key')
121
+ next['x-goog-api-key'] = apiKey;
122
+ else if (profile.auth === 'api-key-header')
123
+ next['api-key'] = apiKey;
124
+ return next;
125
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./tools.js"), exports);
18
+ __exportStar(require("./loop.js"), exports);