@jaypie/llm 1.2.0-rc.2 → 1.2.0-rc.4
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/cjs/Llm.d.ts +7 -0
- package/dist/cjs/index.cjs +811 -3
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/operate/OperateLoop.d.ts +0 -2
- package/dist/cjs/operate/StreamLoop.d.ts +41 -0
- package/dist/cjs/operate/adapters/AnthropicAdapter.d.ts +2 -0
- package/dist/cjs/operate/adapters/GeminiAdapter.d.ts +2 -0
- package/dist/cjs/operate/adapters/OpenAiAdapter.d.ts +2 -0
- package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +2 -0
- package/dist/cjs/operate/adapters/ProviderAdapter.interface.d.ts +9 -0
- package/dist/cjs/operate/index.d.ts +2 -0
- package/dist/cjs/providers/anthropic/AnthropicProvider.class.d.ts +4 -0
- package/dist/cjs/providers/anthropic/utils.d.ts +1 -1
- package/dist/cjs/providers/gemini/GeminiProvider.class.d.ts +4 -0
- package/dist/cjs/providers/gemini/utils.d.ts +1 -1
- package/dist/cjs/providers/openai/OpenAiProvider.class.d.ts +4 -0
- package/dist/cjs/providers/openai/utils.d.ts +1 -1
- package/dist/cjs/providers/openrouter/OpenRouterProvider.class.d.ts +4 -0
- package/dist/cjs/providers/openrouter/utils.d.ts +1 -1
- package/dist/cjs/tools/Toolkit.class.d.ts +1 -1
- package/dist/cjs/types/LlmProvider.interface.d.ts +2 -0
- package/dist/cjs/types/LlmStreamChunk.interface.d.ts +41 -0
- package/dist/cjs/util/logger.d.ts +2 -2
- package/dist/esm/Llm.d.ts +7 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +812 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/OperateLoop.d.ts +0 -2
- package/dist/esm/operate/StreamLoop.d.ts +41 -0
- package/dist/esm/operate/adapters/AnthropicAdapter.d.ts +2 -0
- package/dist/esm/operate/adapters/GeminiAdapter.d.ts +2 -0
- package/dist/esm/operate/adapters/OpenAiAdapter.d.ts +2 -0
- package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +2 -0
- package/dist/esm/operate/adapters/ProviderAdapter.interface.d.ts +9 -0
- package/dist/esm/operate/index.d.ts +2 -0
- package/dist/esm/providers/anthropic/AnthropicProvider.class.d.ts +4 -0
- package/dist/esm/providers/anthropic/utils.d.ts +1 -1
- package/dist/esm/providers/gemini/GeminiProvider.class.d.ts +4 -0
- package/dist/esm/providers/gemini/utils.d.ts +1 -1
- package/dist/esm/providers/openai/OpenAiProvider.class.d.ts +4 -0
- package/dist/esm/providers/openai/utils.d.ts +1 -1
- package/dist/esm/providers/openrouter/OpenRouterProvider.class.d.ts +4 -0
- package/dist/esm/providers/openrouter/utils.d.ts +1 -1
- package/dist/esm/tools/Toolkit.class.d.ts +1 -1
- package/dist/esm/types/LlmProvider.interface.d.ts +2 -0
- package/dist/esm/types/LlmStreamChunk.interface.d.ts +41 -0
- package/dist/esm/util/logger.d.ts +2 -2
- package/package.json +5 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -338,6 +338,19 @@ var LlmResponseStatus;
|
|
|
338
338
|
LlmResponseStatus["InProgress"] = "in_progress";
|
|
339
339
|
})(LlmResponseStatus || (LlmResponseStatus = {}));
|
|
340
340
|
|
|
341
|
+
//
|
|
342
|
+
//
|
|
343
|
+
// Types
|
|
344
|
+
//
|
|
345
|
+
exports.LlmStreamChunkType = void 0;
|
|
346
|
+
(function (LlmStreamChunkType) {
|
|
347
|
+
LlmStreamChunkType["Done"] = "done";
|
|
348
|
+
LlmStreamChunkType["Error"] = "error";
|
|
349
|
+
LlmStreamChunkType["Text"] = "text";
|
|
350
|
+
LlmStreamChunkType["ToolCall"] = "tool_call";
|
|
351
|
+
LlmStreamChunkType["ToolResult"] = "tool_result";
|
|
352
|
+
})(exports.LlmStreamChunkType || (exports.LlmStreamChunkType = {}));
|
|
353
|
+
|
|
341
354
|
/**
|
|
342
355
|
* Converts a string to a standardized LlmInputMessage
|
|
343
356
|
* @param input - String to format
|
|
@@ -815,6 +828,90 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
815
828
|
const anthropic = client;
|
|
816
829
|
return (await anthropic.messages.create(request));
|
|
817
830
|
}
|
|
831
|
+
async *executeStreamRequest(client, request) {
|
|
832
|
+
const anthropic = client;
|
|
833
|
+
const streamRequest = {
|
|
834
|
+
...request,
|
|
835
|
+
stream: true,
|
|
836
|
+
};
|
|
837
|
+
const stream = await anthropic.messages.create(streamRequest);
|
|
838
|
+
// Track current tool call being built
|
|
839
|
+
let currentToolCall = null;
|
|
840
|
+
// Track usage for final chunk
|
|
841
|
+
let inputTokens = 0;
|
|
842
|
+
let outputTokens = 0;
|
|
843
|
+
let model = streamRequest.model;
|
|
844
|
+
for await (const event of stream) {
|
|
845
|
+
if (event.type === "message_start") {
|
|
846
|
+
// Extract initial usage and model info
|
|
847
|
+
const message = event.message;
|
|
848
|
+
if (message.usage) {
|
|
849
|
+
inputTokens = message.usage.input_tokens;
|
|
850
|
+
}
|
|
851
|
+
model = message.model;
|
|
852
|
+
}
|
|
853
|
+
else if (event.type === "content_block_start") {
|
|
854
|
+
const contentBlock = event.content_block;
|
|
855
|
+
if (contentBlock.type === "tool_use") {
|
|
856
|
+
// Start building a tool call
|
|
857
|
+
currentToolCall = {
|
|
858
|
+
id: contentBlock.id,
|
|
859
|
+
name: contentBlock.name,
|
|
860
|
+
arguments: "",
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else if (event.type === "content_block_delta") {
|
|
865
|
+
const delta = event.delta;
|
|
866
|
+
if (delta.type === "text_delta") {
|
|
867
|
+
yield {
|
|
868
|
+
type: exports.LlmStreamChunkType.Text,
|
|
869
|
+
content: delta.text,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
else if (delta.type === "input_json_delta" && currentToolCall) {
|
|
873
|
+
// Accumulate tool call arguments
|
|
874
|
+
currentToolCall.arguments += delta.partial_json;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
else if (event.type === "content_block_stop") {
|
|
878
|
+
// If we were building a tool call, emit it now
|
|
879
|
+
if (currentToolCall) {
|
|
880
|
+
yield {
|
|
881
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
882
|
+
toolCall: {
|
|
883
|
+
id: currentToolCall.id,
|
|
884
|
+
name: currentToolCall.name,
|
|
885
|
+
arguments: currentToolCall.arguments,
|
|
886
|
+
},
|
|
887
|
+
};
|
|
888
|
+
currentToolCall = null;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
else if (event.type === "message_delta") {
|
|
892
|
+
// Extract final usage
|
|
893
|
+
if (event.usage) {
|
|
894
|
+
outputTokens = event.usage.output_tokens;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
else if (event.type === "message_stop") {
|
|
898
|
+
// Emit done chunk with usage
|
|
899
|
+
yield {
|
|
900
|
+
type: exports.LlmStreamChunkType.Done,
|
|
901
|
+
usage: [
|
|
902
|
+
{
|
|
903
|
+
input: inputTokens,
|
|
904
|
+
output: outputTokens,
|
|
905
|
+
reasoning: 0,
|
|
906
|
+
total: inputTokens + outputTokens,
|
|
907
|
+
provider: this.name,
|
|
908
|
+
model,
|
|
909
|
+
},
|
|
910
|
+
],
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
818
915
|
//
|
|
819
916
|
// Response Parsing
|
|
820
917
|
//
|
|
@@ -1147,6 +1244,77 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1147
1244
|
});
|
|
1148
1245
|
return response;
|
|
1149
1246
|
}
|
|
1247
|
+
async *executeStreamRequest(client, request) {
|
|
1248
|
+
const genAI = client;
|
|
1249
|
+
const geminiRequest = request;
|
|
1250
|
+
// Use generateContentStream for streaming
|
|
1251
|
+
const stream = await genAI.models.generateContentStream({
|
|
1252
|
+
model: geminiRequest.model,
|
|
1253
|
+
contents: geminiRequest.contents,
|
|
1254
|
+
config: geminiRequest.config,
|
|
1255
|
+
});
|
|
1256
|
+
// Track current function call being built
|
|
1257
|
+
let currentFunctionCall = null;
|
|
1258
|
+
// Track usage for final chunk
|
|
1259
|
+
let inputTokens = 0;
|
|
1260
|
+
let outputTokens = 0;
|
|
1261
|
+
let reasoningTokens = 0;
|
|
1262
|
+
const model = geminiRequest.model || this.defaultModel;
|
|
1263
|
+
for await (const chunk of stream) {
|
|
1264
|
+
// Extract text content from the chunk
|
|
1265
|
+
const candidate = chunk.candidates?.[0];
|
|
1266
|
+
if (candidate?.content?.parts) {
|
|
1267
|
+
for (const part of candidate.content.parts) {
|
|
1268
|
+
// Handle text content (excluding thought parts)
|
|
1269
|
+
if (part.text && !part.thought) {
|
|
1270
|
+
yield {
|
|
1271
|
+
type: exports.LlmStreamChunkType.Text,
|
|
1272
|
+
content: part.text,
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
// Handle function calls
|
|
1276
|
+
if (part.functionCall) {
|
|
1277
|
+
const functionCall = part.functionCall;
|
|
1278
|
+
currentFunctionCall = {
|
|
1279
|
+
id: functionCall.id || this.generateCallId(),
|
|
1280
|
+
name: functionCall.name || "",
|
|
1281
|
+
arguments: functionCall.args || {},
|
|
1282
|
+
};
|
|
1283
|
+
// Emit the function call immediately
|
|
1284
|
+
yield {
|
|
1285
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
1286
|
+
toolCall: {
|
|
1287
|
+
id: currentFunctionCall.id,
|
|
1288
|
+
name: currentFunctionCall.name,
|
|
1289
|
+
arguments: JSON.stringify(currentFunctionCall.arguments),
|
|
1290
|
+
},
|
|
1291
|
+
};
|
|
1292
|
+
currentFunctionCall = null;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
// Extract usage metadata if present
|
|
1297
|
+
if (chunk.usageMetadata) {
|
|
1298
|
+
inputTokens = chunk.usageMetadata.promptTokenCount || 0;
|
|
1299
|
+
outputTokens = chunk.usageMetadata.candidatesTokenCount || 0;
|
|
1300
|
+
reasoningTokens = chunk.usageMetadata.thoughtsTokenCount || 0;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
// Emit done chunk with final usage
|
|
1304
|
+
yield {
|
|
1305
|
+
type: exports.LlmStreamChunkType.Done,
|
|
1306
|
+
usage: [
|
|
1307
|
+
{
|
|
1308
|
+
input: inputTokens,
|
|
1309
|
+
output: outputTokens,
|
|
1310
|
+
reasoning: reasoningTokens,
|
|
1311
|
+
total: inputTokens + outputTokens,
|
|
1312
|
+
provider: this.name,
|
|
1313
|
+
model,
|
|
1314
|
+
},
|
|
1315
|
+
],
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1150
1318
|
//
|
|
1151
1319
|
// Response Parsing
|
|
1152
1320
|
//
|
|
@@ -1612,6 +1780,95 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
1612
1780
|
// @ts-expect-error OpenAI SDK types don't match our request format exactly
|
|
1613
1781
|
return await openai.responses.create(request);
|
|
1614
1782
|
}
|
|
1783
|
+
async *executeStreamRequest(client, request) {
|
|
1784
|
+
const openai = client;
|
|
1785
|
+
const baseRequest = request;
|
|
1786
|
+
const streamRequest = {
|
|
1787
|
+
...baseRequest,
|
|
1788
|
+
stream: true,
|
|
1789
|
+
};
|
|
1790
|
+
const stream = await openai.responses.create(streamRequest);
|
|
1791
|
+
// Track current function call being built
|
|
1792
|
+
let currentFunctionCall = null;
|
|
1793
|
+
// Track usage for final chunk
|
|
1794
|
+
let inputTokens = 0;
|
|
1795
|
+
let outputTokens = 0;
|
|
1796
|
+
let reasoningTokens = 0;
|
|
1797
|
+
const model = baseRequest.model || this.defaultModel;
|
|
1798
|
+
// Cast to async iterable - when stream: true, this is always a Stream<ResponseStreamEvent>
|
|
1799
|
+
const asyncStream = stream;
|
|
1800
|
+
for await (const event of asyncStream) {
|
|
1801
|
+
const eventType = event.type;
|
|
1802
|
+
if (eventType === "response.output_text.delta") {
|
|
1803
|
+
// Text content delta
|
|
1804
|
+
const delta = event.delta;
|
|
1805
|
+
if (delta) {
|
|
1806
|
+
yield {
|
|
1807
|
+
type: exports.LlmStreamChunkType.Text,
|
|
1808
|
+
content: delta,
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
else if (eventType === "response.function_call_arguments.delta") {
|
|
1813
|
+
// Function call arguments delta - accumulate
|
|
1814
|
+
const delta = event.delta;
|
|
1815
|
+
if (delta && currentFunctionCall) {
|
|
1816
|
+
currentFunctionCall.arguments += delta;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
else if (eventType === "response.output_item.added") {
|
|
1820
|
+
// New output item - check if it's a function call
|
|
1821
|
+
const item = event.item;
|
|
1822
|
+
if (item?.type === "function_call") {
|
|
1823
|
+
currentFunctionCall = {
|
|
1824
|
+
id: item.id || "",
|
|
1825
|
+
callId: item.call_id || "",
|
|
1826
|
+
name: item.name || "",
|
|
1827
|
+
arguments: "",
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
else if (eventType === "response.output_item.done") {
|
|
1832
|
+
// Output item completed - emit function call if that's what we were building
|
|
1833
|
+
if (currentFunctionCall) {
|
|
1834
|
+
yield {
|
|
1835
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
1836
|
+
toolCall: {
|
|
1837
|
+
id: currentFunctionCall.callId,
|
|
1838
|
+
name: currentFunctionCall.name,
|
|
1839
|
+
arguments: currentFunctionCall.arguments,
|
|
1840
|
+
},
|
|
1841
|
+
};
|
|
1842
|
+
currentFunctionCall = null;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
else if (eventType === "response.completed") {
|
|
1846
|
+
// Response completed - extract final usage
|
|
1847
|
+
const response = event.response;
|
|
1848
|
+
if (response?.usage) {
|
|
1849
|
+
inputTokens = response.usage.input_tokens || 0;
|
|
1850
|
+
outputTokens = response.usage.output_tokens || 0;
|
|
1851
|
+
reasoningTokens = response.usage.output_tokens_details?.reasoning_tokens || 0;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
else if (eventType === "response.done") {
|
|
1855
|
+
// Stream done - emit final chunk with usage
|
|
1856
|
+
yield {
|
|
1857
|
+
type: exports.LlmStreamChunkType.Done,
|
|
1858
|
+
usage: [
|
|
1859
|
+
{
|
|
1860
|
+
input: inputTokens,
|
|
1861
|
+
output: outputTokens,
|
|
1862
|
+
reasoning: reasoningTokens,
|
|
1863
|
+
total: inputTokens + outputTokens,
|
|
1864
|
+
provider: this.name,
|
|
1865
|
+
model,
|
|
1866
|
+
},
|
|
1867
|
+
],
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1615
1872
|
//
|
|
1616
1873
|
// Response Parsing
|
|
1617
1874
|
//
|
|
@@ -1924,6 +2181,110 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
1924
2181
|
});
|
|
1925
2182
|
return response;
|
|
1926
2183
|
}
|
|
2184
|
+
async *executeStreamRequest(client, request) {
|
|
2185
|
+
const openRouter = client;
|
|
2186
|
+
const openRouterRequest = request;
|
|
2187
|
+
// Use chat.send with stream: true for streaming responses
|
|
2188
|
+
const stream = await openRouter.chat.send({
|
|
2189
|
+
model: openRouterRequest.model,
|
|
2190
|
+
messages: openRouterRequest.messages,
|
|
2191
|
+
tools: openRouterRequest.tools,
|
|
2192
|
+
toolChoice: openRouterRequest.tool_choice,
|
|
2193
|
+
user: openRouterRequest.user,
|
|
2194
|
+
stream: true,
|
|
2195
|
+
});
|
|
2196
|
+
// Track current tool call being built
|
|
2197
|
+
let currentToolCall = null;
|
|
2198
|
+
// Track usage for final chunk
|
|
2199
|
+
let inputTokens = 0;
|
|
2200
|
+
let outputTokens = 0;
|
|
2201
|
+
const model = openRouterRequest.model || this.defaultModel;
|
|
2202
|
+
for await (const chunk of stream) {
|
|
2203
|
+
const typedChunk = chunk;
|
|
2204
|
+
const choices = typedChunk.choices;
|
|
2205
|
+
if (choices && choices.length > 0) {
|
|
2206
|
+
const delta = choices[0].delta;
|
|
2207
|
+
// Handle text content
|
|
2208
|
+
if (delta?.content) {
|
|
2209
|
+
yield {
|
|
2210
|
+
type: exports.LlmStreamChunkType.Text,
|
|
2211
|
+
content: delta.content,
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
// Handle tool calls
|
|
2215
|
+
if (delta?.tool_calls && delta.tool_calls.length > 0) {
|
|
2216
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
2217
|
+
if (toolCallDelta.id) {
|
|
2218
|
+
// New tool call starting
|
|
2219
|
+
if (currentToolCall) {
|
|
2220
|
+
// Emit the previous tool call
|
|
2221
|
+
yield {
|
|
2222
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
2223
|
+
toolCall: {
|
|
2224
|
+
id: currentToolCall.id,
|
|
2225
|
+
name: currentToolCall.name,
|
|
2226
|
+
arguments: currentToolCall.arguments,
|
|
2227
|
+
},
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
currentToolCall = {
|
|
2231
|
+
id: toolCallDelta.id,
|
|
2232
|
+
name: toolCallDelta.function?.name || "",
|
|
2233
|
+
arguments: toolCallDelta.function?.arguments || "",
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
else if (currentToolCall) {
|
|
2237
|
+
// Continuing existing tool call
|
|
2238
|
+
if (toolCallDelta.function?.name) {
|
|
2239
|
+
currentToolCall.name += toolCallDelta.function.name;
|
|
2240
|
+
}
|
|
2241
|
+
if (toolCallDelta.function?.arguments) {
|
|
2242
|
+
currentToolCall.arguments += toolCallDelta.function.arguments;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
// Check for finish reason
|
|
2248
|
+
if (choices[0].finish_reason) {
|
|
2249
|
+
// Emit any pending tool call
|
|
2250
|
+
if (currentToolCall) {
|
|
2251
|
+
yield {
|
|
2252
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
2253
|
+
toolCall: {
|
|
2254
|
+
id: currentToolCall.id,
|
|
2255
|
+
name: currentToolCall.name,
|
|
2256
|
+
arguments: currentToolCall.arguments,
|
|
2257
|
+
},
|
|
2258
|
+
};
|
|
2259
|
+
currentToolCall = null;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
// Extract usage if present (usually in the final chunk)
|
|
2264
|
+
if (typedChunk.usage) {
|
|
2265
|
+
inputTokens =
|
|
2266
|
+
typedChunk.usage.prompt_tokens || typedChunk.usage.promptTokens || 0;
|
|
2267
|
+
outputTokens =
|
|
2268
|
+
typedChunk.usage.completion_tokens ||
|
|
2269
|
+
typedChunk.usage.completionTokens ||
|
|
2270
|
+
0;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
// Emit done chunk with final usage
|
|
2274
|
+
yield {
|
|
2275
|
+
type: exports.LlmStreamChunkType.Done,
|
|
2276
|
+
usage: [
|
|
2277
|
+
{
|
|
2278
|
+
input: inputTokens,
|
|
2279
|
+
output: outputTokens,
|
|
2280
|
+
reasoning: 0,
|
|
2281
|
+
total: inputTokens + outputTokens,
|
|
2282
|
+
provider: this.name,
|
|
2283
|
+
model,
|
|
2284
|
+
},
|
|
2285
|
+
],
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
1927
2288
|
//
|
|
1928
2289
|
// Response Parsing
|
|
1929
2290
|
//
|
|
@@ -2771,7 +3132,7 @@ class RetryExecutor {
|
|
|
2771
3132
|
//
|
|
2772
3133
|
// Constants
|
|
2773
3134
|
//
|
|
2774
|
-
const ERROR = {
|
|
3135
|
+
const ERROR$1 = {
|
|
2775
3136
|
BAD_FUNCTION_CALL: "Bad Function Call",
|
|
2776
3137
|
};
|
|
2777
3138
|
//
|
|
@@ -2808,13 +3169,16 @@ class OperateLoop {
|
|
|
2808
3169
|
this.client = config.client;
|
|
2809
3170
|
this.hookRunnerInstance = config.hookRunner ?? hookRunner;
|
|
2810
3171
|
this.inputProcessorInstance = config.inputProcessor ?? inputProcessor;
|
|
2811
|
-
this.maxRetries = config.maxRetries ?? 6;
|
|
2812
3172
|
this.retryPolicy = config.retryPolicy ?? defaultRetryPolicy;
|
|
2813
3173
|
}
|
|
2814
3174
|
/**
|
|
2815
3175
|
* Execute the operate loop for multi-turn conversations with tool calling.
|
|
2816
3176
|
*/
|
|
2817
3177
|
async execute(input, options = {}) {
|
|
3178
|
+
// Log what was passed to operate
|
|
3179
|
+
log$1.trace("[operate] Starting operate loop");
|
|
3180
|
+
log$1.var({ "operate.input": input });
|
|
3181
|
+
log$1.var({ "operate.options": options });
|
|
2818
3182
|
// Initialize state
|
|
2819
3183
|
const state = this.initializeState(input, options);
|
|
2820
3184
|
const context = this.createContext(options);
|
|
@@ -2917,6 +3281,9 @@ class OperateLoop {
|
|
|
2917
3281
|
});
|
|
2918
3282
|
// Build provider-specific request
|
|
2919
3283
|
const providerRequest = this.adapter.buildRequest(request);
|
|
3284
|
+
// Log what was passed to the model
|
|
3285
|
+
log$1.trace("[operate] Calling model");
|
|
3286
|
+
log$1.var({ "operate.request": providerRequest });
|
|
2920
3287
|
// Execute beforeEachModelRequest hook
|
|
2921
3288
|
await this.hookRunnerInstance.runBeforeModelRequest(context.hooks, {
|
|
2922
3289
|
input: state.currentInput,
|
|
@@ -2932,6 +3299,9 @@ class OperateLoop {
|
|
|
2932
3299
|
},
|
|
2933
3300
|
hooks: context.hooks,
|
|
2934
3301
|
});
|
|
3302
|
+
// Log what was returned from the model
|
|
3303
|
+
log$1.trace("[operate] Model response received");
|
|
3304
|
+
log$1.var({ "operate.response": response });
|
|
2935
3305
|
// Parse response
|
|
2936
3306
|
const parsed = this.adapter.parseResponse(response, options);
|
|
2937
3307
|
// Track usage
|
|
@@ -3020,7 +3390,7 @@ class OperateLoop {
|
|
|
3020
3390
|
state.responseBuilder.setError({
|
|
3021
3391
|
detail,
|
|
3022
3392
|
status: jaypieError.status,
|
|
3023
|
-
title: ERROR.BAD_FUNCTION_CALL,
|
|
3393
|
+
title: ERROR$1.BAD_FUNCTION_CALL,
|
|
3024
3394
|
});
|
|
3025
3395
|
log$1.error(`Error executing function call ${toolCall.name}`);
|
|
3026
3396
|
log$1.var({ error });
|
|
@@ -3151,6 +3521,325 @@ function createOperateLoop(config) {
|
|
|
3151
3521
|
return new OperateLoop(config);
|
|
3152
3522
|
}
|
|
3153
3523
|
|
|
3524
|
+
//
|
|
3525
|
+
//
|
|
3526
|
+
// Constants
|
|
3527
|
+
//
|
|
3528
|
+
const ERROR = {
|
|
3529
|
+
BAD_FUNCTION_CALL: "Bad Function Call",
|
|
3530
|
+
};
|
|
3531
|
+
//
|
|
3532
|
+
//
|
|
3533
|
+
// Main
|
|
3534
|
+
//
|
|
3535
|
+
/**
|
|
3536
|
+
* StreamLoop implements streaming multi-turn conversation loop.
|
|
3537
|
+
* It orchestrates provider adapters and tool calling while yielding
|
|
3538
|
+
* stream chunks as they become available.
|
|
3539
|
+
*/
|
|
3540
|
+
class StreamLoop {
|
|
3541
|
+
constructor(config) {
|
|
3542
|
+
this.adapter = config.adapter;
|
|
3543
|
+
this.client = config.client;
|
|
3544
|
+
this.hookRunnerInstance = config.hookRunner ?? hookRunner;
|
|
3545
|
+
this.inputProcessorInstance = config.inputProcessor ?? inputProcessor;
|
|
3546
|
+
}
|
|
3547
|
+
/**
|
|
3548
|
+
* Execute the streaming loop for multi-turn conversations with tool calling.
|
|
3549
|
+
* Yields stream chunks as they become available.
|
|
3550
|
+
*/
|
|
3551
|
+
async *execute(input, options = {}) {
|
|
3552
|
+
// Verify adapter supports streaming
|
|
3553
|
+
if (!this.adapter.executeStreamRequest) {
|
|
3554
|
+
throw new errors.BadGatewayError(`Provider ${this.adapter.name} does not support streaming`);
|
|
3555
|
+
}
|
|
3556
|
+
// Initialize state
|
|
3557
|
+
const state = this.initializeState(input, options);
|
|
3558
|
+
const context = this.createContext(options);
|
|
3559
|
+
// Build initial request
|
|
3560
|
+
let request = this.buildInitialRequest(state, options);
|
|
3561
|
+
// Multi-turn loop
|
|
3562
|
+
while (state.currentTurn < state.maxTurns) {
|
|
3563
|
+
state.currentTurn++;
|
|
3564
|
+
// Execute one streaming turn
|
|
3565
|
+
const { shouldContinue, toolCalls } = yield* this.executeOneStreamingTurn(request, state, context, options);
|
|
3566
|
+
if (!shouldContinue) {
|
|
3567
|
+
break;
|
|
3568
|
+
}
|
|
3569
|
+
// If we have tool calls, process them
|
|
3570
|
+
if (toolCalls && toolCalls.length > 0 && state.toolkit) {
|
|
3571
|
+
yield* this.processToolCalls(toolCalls, state, context, options);
|
|
3572
|
+
// Check if we've reached max turns
|
|
3573
|
+
if (state.currentTurn >= state.maxTurns) {
|
|
3574
|
+
const error = new errors.TooManyRequestsError();
|
|
3575
|
+
const detail = `Model requested function call but exceeded ${state.maxTurns} turns`;
|
|
3576
|
+
log$1.warn(detail);
|
|
3577
|
+
yield {
|
|
3578
|
+
type: exports.LlmStreamChunkType.Error,
|
|
3579
|
+
error: {
|
|
3580
|
+
detail,
|
|
3581
|
+
status: error.status,
|
|
3582
|
+
title: error.title,
|
|
3583
|
+
},
|
|
3584
|
+
};
|
|
3585
|
+
break;
|
|
3586
|
+
}
|
|
3587
|
+
// Rebuild request with updated history for next turn
|
|
3588
|
+
request = {
|
|
3589
|
+
format: state.formattedFormat,
|
|
3590
|
+
instructions: options.instructions,
|
|
3591
|
+
messages: state.currentInput,
|
|
3592
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
3593
|
+
providerOptions: options.providerOptions,
|
|
3594
|
+
system: options.system,
|
|
3595
|
+
tools: state.formattedTools,
|
|
3596
|
+
user: options.user,
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
else {
|
|
3600
|
+
break;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
// Emit final done chunk with accumulated usage
|
|
3604
|
+
yield {
|
|
3605
|
+
type: exports.LlmStreamChunkType.Done,
|
|
3606
|
+
usage: state.usageItems,
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
//
|
|
3610
|
+
// Private Methods
|
|
3611
|
+
//
|
|
3612
|
+
initializeState(input, options) {
|
|
3613
|
+
// Process input with placeholders
|
|
3614
|
+
const processedInput = this.inputProcessorInstance.process(input, options);
|
|
3615
|
+
// Determine max turns
|
|
3616
|
+
const maxTurns = maxTurnsFromOptions(options);
|
|
3617
|
+
// Get toolkit
|
|
3618
|
+
let toolkit;
|
|
3619
|
+
if (options.tools) {
|
|
3620
|
+
if (options.tools instanceof Toolkit) {
|
|
3621
|
+
toolkit = options.tools;
|
|
3622
|
+
}
|
|
3623
|
+
else if (Array.isArray(options.tools) && options.tools.length > 0) {
|
|
3624
|
+
const explain = options.explain ?? false;
|
|
3625
|
+
toolkit = new Toolkit(options.tools, { explain });
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
// Format output schema through adapter if provided
|
|
3629
|
+
let formattedFormat;
|
|
3630
|
+
if (options.format) {
|
|
3631
|
+
formattedFormat = this.adapter.formatOutputSchema(options.format);
|
|
3632
|
+
}
|
|
3633
|
+
// Format tools through adapter
|
|
3634
|
+
const formattedTools = toolkit
|
|
3635
|
+
? this.adapter.formatTools(toolkit, formattedFormat)
|
|
3636
|
+
: undefined;
|
|
3637
|
+
return {
|
|
3638
|
+
currentInput: processedInput.history,
|
|
3639
|
+
currentTurn: 0,
|
|
3640
|
+
formattedFormat,
|
|
3641
|
+
formattedTools,
|
|
3642
|
+
maxTurns,
|
|
3643
|
+
toolkit,
|
|
3644
|
+
usageItems: [],
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
createContext(options) {
|
|
3648
|
+
return {
|
|
3649
|
+
hooks: options.hooks ?? {},
|
|
3650
|
+
options,
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
buildInitialRequest(state, options) {
|
|
3654
|
+
return {
|
|
3655
|
+
format: state.formattedFormat,
|
|
3656
|
+
instructions: options.instructions,
|
|
3657
|
+
messages: state.currentInput,
|
|
3658
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
3659
|
+
providerOptions: options.providerOptions,
|
|
3660
|
+
system: options.system,
|
|
3661
|
+
tools: state.formattedTools,
|
|
3662
|
+
user: options.user,
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
async *executeOneStreamingTurn(request, state, context, options) {
|
|
3666
|
+
// Build provider-specific request
|
|
3667
|
+
const providerRequest = this.adapter.buildRequest(request);
|
|
3668
|
+
// Execute beforeEachModelRequest hook
|
|
3669
|
+
await this.hookRunnerInstance.runBeforeModelRequest(context.hooks, {
|
|
3670
|
+
input: state.currentInput,
|
|
3671
|
+
options,
|
|
3672
|
+
providerRequest,
|
|
3673
|
+
});
|
|
3674
|
+
// Collect tool calls from the stream
|
|
3675
|
+
const collectedToolCalls = [];
|
|
3676
|
+
// Execute streaming request
|
|
3677
|
+
const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest);
|
|
3678
|
+
for await (const chunk of streamGenerator) {
|
|
3679
|
+
// Pass through text chunks
|
|
3680
|
+
if (chunk.type === exports.LlmStreamChunkType.Text) {
|
|
3681
|
+
yield chunk;
|
|
3682
|
+
}
|
|
3683
|
+
// Collect tool calls
|
|
3684
|
+
if (chunk.type === exports.LlmStreamChunkType.ToolCall) {
|
|
3685
|
+
collectedToolCalls.push({
|
|
3686
|
+
callId: chunk.toolCall.id,
|
|
3687
|
+
name: chunk.toolCall.name,
|
|
3688
|
+
arguments: chunk.toolCall.arguments,
|
|
3689
|
+
raw: chunk.toolCall,
|
|
3690
|
+
});
|
|
3691
|
+
yield chunk;
|
|
3692
|
+
}
|
|
3693
|
+
// Track usage from done chunk (but don't yield it yet - we'll emit our own)
|
|
3694
|
+
if (chunk.type === exports.LlmStreamChunkType.Done && chunk.usage) {
|
|
3695
|
+
state.usageItems.push(...chunk.usage);
|
|
3696
|
+
}
|
|
3697
|
+
// Pass through error chunks
|
|
3698
|
+
if (chunk.type === exports.LlmStreamChunkType.Error) {
|
|
3699
|
+
yield chunk;
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
// Execute afterEachModelResponse hook
|
|
3703
|
+
await this.hookRunnerInstance.runAfterModelResponse(context.hooks, {
|
|
3704
|
+
content: "",
|
|
3705
|
+
input: state.currentInput,
|
|
3706
|
+
options,
|
|
3707
|
+
providerRequest,
|
|
3708
|
+
providerResponse: null,
|
|
3709
|
+
usage: state.usageItems,
|
|
3710
|
+
});
|
|
3711
|
+
// If we have tool calls and a toolkit, continue the loop
|
|
3712
|
+
if (collectedToolCalls.length > 0 && state.toolkit && state.maxTurns > 1) {
|
|
3713
|
+
// Add tool calls to history
|
|
3714
|
+
for (const toolCall of collectedToolCalls) {
|
|
3715
|
+
state.currentInput.push({
|
|
3716
|
+
type: exports.LlmMessageType.FunctionCall,
|
|
3717
|
+
name: toolCall.name,
|
|
3718
|
+
arguments: toolCall.arguments,
|
|
3719
|
+
call_id: toolCall.callId,
|
|
3720
|
+
id: toolCall.callId,
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
return { shouldContinue: true, toolCalls: collectedToolCalls };
|
|
3724
|
+
}
|
|
3725
|
+
return { shouldContinue: false };
|
|
3726
|
+
}
|
|
3727
|
+
async *processToolCalls(toolCalls, state, context, _options) {
|
|
3728
|
+
for (const toolCall of toolCalls) {
|
|
3729
|
+
try {
|
|
3730
|
+
// Execute beforeEachTool hook
|
|
3731
|
+
await this.hookRunnerInstance.runBeforeTool(context.hooks, {
|
|
3732
|
+
args: toolCall.arguments,
|
|
3733
|
+
toolName: toolCall.name,
|
|
3734
|
+
});
|
|
3735
|
+
// Call the tool
|
|
3736
|
+
log$1.trace(`[stream] Calling tool - ${toolCall.name}`);
|
|
3737
|
+
const result = await state.toolkit.call({
|
|
3738
|
+
arguments: toolCall.arguments,
|
|
3739
|
+
name: toolCall.name,
|
|
3740
|
+
});
|
|
3741
|
+
// Execute afterEachTool hook
|
|
3742
|
+
await this.hookRunnerInstance.runAfterTool(context.hooks, {
|
|
3743
|
+
args: toolCall.arguments,
|
|
3744
|
+
result,
|
|
3745
|
+
toolName: toolCall.name,
|
|
3746
|
+
});
|
|
3747
|
+
// Yield tool result chunk
|
|
3748
|
+
yield {
|
|
3749
|
+
type: exports.LlmStreamChunkType.ToolResult,
|
|
3750
|
+
toolResult: {
|
|
3751
|
+
id: toolCall.callId,
|
|
3752
|
+
name: toolCall.name,
|
|
3753
|
+
result,
|
|
3754
|
+
},
|
|
3755
|
+
};
|
|
3756
|
+
// Add tool result to history
|
|
3757
|
+
state.currentInput.push({
|
|
3758
|
+
type: exports.LlmMessageType.FunctionCallOutput,
|
|
3759
|
+
output: JSON.stringify(result),
|
|
3760
|
+
call_id: toolCall.callId,
|
|
3761
|
+
name: toolCall.name,
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
catch (error) {
|
|
3765
|
+
// Execute onToolError hook
|
|
3766
|
+
await this.hookRunnerInstance.runOnToolError(context.hooks, {
|
|
3767
|
+
args: toolCall.arguments,
|
|
3768
|
+
error: error,
|
|
3769
|
+
toolName: toolCall.name,
|
|
3770
|
+
});
|
|
3771
|
+
// Yield error chunk
|
|
3772
|
+
const jaypieError = new errors.BadGatewayError();
|
|
3773
|
+
const detail = [
|
|
3774
|
+
`Error executing function call ${toolCall.name}.`,
|
|
3775
|
+
error.message,
|
|
3776
|
+
].join("\n");
|
|
3777
|
+
yield {
|
|
3778
|
+
type: exports.LlmStreamChunkType.Error,
|
|
3779
|
+
error: {
|
|
3780
|
+
detail,
|
|
3781
|
+
status: jaypieError.status,
|
|
3782
|
+
title: ERROR.BAD_FUNCTION_CALL,
|
|
3783
|
+
},
|
|
3784
|
+
};
|
|
3785
|
+
log$1.error(`Error executing function call ${toolCall.name}`);
|
|
3786
|
+
log$1.var({ error });
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Convert Gemini contents format to internal history format.
|
|
3792
|
+
*/
|
|
3793
|
+
convertGeminiContentsToHistory(contents) {
|
|
3794
|
+
const history = [];
|
|
3795
|
+
for (const content of contents) {
|
|
3796
|
+
if (!content.parts)
|
|
3797
|
+
continue;
|
|
3798
|
+
for (const part of content.parts) {
|
|
3799
|
+
if (part.text && typeof part.text === "string") {
|
|
3800
|
+
history.push({
|
|
3801
|
+
role: content.role === "model"
|
|
3802
|
+
? exports.LlmMessageRole.Assistant
|
|
3803
|
+
: exports.LlmMessageRole.User,
|
|
3804
|
+
content: part.text,
|
|
3805
|
+
type: exports.LlmMessageType.Message,
|
|
3806
|
+
});
|
|
3807
|
+
}
|
|
3808
|
+
else if (part.functionCall) {
|
|
3809
|
+
const fc = part.functionCall;
|
|
3810
|
+
history.push({
|
|
3811
|
+
type: exports.LlmMessageType.FunctionCall,
|
|
3812
|
+
name: fc.name || "",
|
|
3813
|
+
arguments: JSON.stringify(fc.args || {}),
|
|
3814
|
+
call_id: fc.id || "",
|
|
3815
|
+
id: fc.id || "",
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3818
|
+
else if (part.functionResponse) {
|
|
3819
|
+
const fr = part.functionResponse;
|
|
3820
|
+
history.push({
|
|
3821
|
+
type: exports.LlmMessageType.FunctionCallOutput,
|
|
3822
|
+
output: JSON.stringify(fr.response || {}),
|
|
3823
|
+
call_id: "",
|
|
3824
|
+
name: fr.name || "",
|
|
3825
|
+
});
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
return history;
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
//
|
|
3833
|
+
//
|
|
3834
|
+
// Factory
|
|
3835
|
+
//
|
|
3836
|
+
/**
|
|
3837
|
+
* Create a StreamLoop instance with the specified configuration.
|
|
3838
|
+
*/
|
|
3839
|
+
function createStreamLoop(config) {
|
|
3840
|
+
return new StreamLoop(config);
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3154
3843
|
// SDK loader with caching
|
|
3155
3844
|
let cachedSdk$2 = null;
|
|
3156
3845
|
async function loadSdk$2() {
|
|
@@ -3306,6 +3995,17 @@ class AnthropicProvider {
|
|
|
3306
3995
|
});
|
|
3307
3996
|
return this._operateLoop;
|
|
3308
3997
|
}
|
|
3998
|
+
async getStreamLoop() {
|
|
3999
|
+
if (this._streamLoop) {
|
|
4000
|
+
return this._streamLoop;
|
|
4001
|
+
}
|
|
4002
|
+
const client = await this.getClient();
|
|
4003
|
+
this._streamLoop = createStreamLoop({
|
|
4004
|
+
adapter: anthropicAdapter,
|
|
4005
|
+
client,
|
|
4006
|
+
});
|
|
4007
|
+
return this._streamLoop;
|
|
4008
|
+
}
|
|
3309
4009
|
// Main send method
|
|
3310
4010
|
async send(message, options) {
|
|
3311
4011
|
const client = await this.getClient();
|
|
@@ -3342,6 +4042,18 @@ class AnthropicProvider {
|
|
|
3342
4042
|
}
|
|
3343
4043
|
return response;
|
|
3344
4044
|
}
|
|
4045
|
+
async *stream(input, options = {}) {
|
|
4046
|
+
const streamLoop = await this.getStreamLoop();
|
|
4047
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4048
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4049
|
+
if (this.conversationHistory.length > 0) {
|
|
4050
|
+
mergedOptions.history = options.history
|
|
4051
|
+
? [...this.conversationHistory, ...options.history]
|
|
4052
|
+
: [...this.conversationHistory];
|
|
4053
|
+
}
|
|
4054
|
+
// Execute stream loop
|
|
4055
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4056
|
+
}
|
|
3345
4057
|
}
|
|
3346
4058
|
|
|
3347
4059
|
// SDK loader with caching
|
|
@@ -3418,6 +4130,17 @@ class GeminiProvider {
|
|
|
3418
4130
|
});
|
|
3419
4131
|
return this._operateLoop;
|
|
3420
4132
|
}
|
|
4133
|
+
async getStreamLoop() {
|
|
4134
|
+
if (this._streamLoop) {
|
|
4135
|
+
return this._streamLoop;
|
|
4136
|
+
}
|
|
4137
|
+
const client = await this.getClient();
|
|
4138
|
+
this._streamLoop = createStreamLoop({
|
|
4139
|
+
adapter: geminiAdapter,
|
|
4140
|
+
client,
|
|
4141
|
+
});
|
|
4142
|
+
return this._streamLoop;
|
|
4143
|
+
}
|
|
3421
4144
|
async send(message, options) {
|
|
3422
4145
|
const client = await this.getClient();
|
|
3423
4146
|
const { messages } = prepareMessages$2(message, options);
|
|
@@ -3472,6 +4195,18 @@ class GeminiProvider {
|
|
|
3472
4195
|
}
|
|
3473
4196
|
return response;
|
|
3474
4197
|
}
|
|
4198
|
+
async *stream(input, options = {}) {
|
|
4199
|
+
const streamLoop = await this.getStreamLoop();
|
|
4200
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4201
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4202
|
+
if (this.conversationHistory.length > 0) {
|
|
4203
|
+
mergedOptions.history = options.history
|
|
4204
|
+
? [...this.conversationHistory, ...options.history]
|
|
4205
|
+
: [...this.conversationHistory];
|
|
4206
|
+
}
|
|
4207
|
+
// Execute stream loop
|
|
4208
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4209
|
+
}
|
|
3475
4210
|
}
|
|
3476
4211
|
|
|
3477
4212
|
// Logger
|
|
@@ -3586,6 +4321,17 @@ class OpenAiProvider {
|
|
|
3586
4321
|
});
|
|
3587
4322
|
return this._operateLoop;
|
|
3588
4323
|
}
|
|
4324
|
+
async getStreamLoop() {
|
|
4325
|
+
if (this._streamLoop) {
|
|
4326
|
+
return this._streamLoop;
|
|
4327
|
+
}
|
|
4328
|
+
const client = await this.getClient();
|
|
4329
|
+
this._streamLoop = createStreamLoop({
|
|
4330
|
+
adapter: openAiAdapter,
|
|
4331
|
+
client,
|
|
4332
|
+
});
|
|
4333
|
+
return this._streamLoop;
|
|
4334
|
+
}
|
|
3589
4335
|
async send(message, options) {
|
|
3590
4336
|
const client = await this.getClient();
|
|
3591
4337
|
const messages = prepareMessages$1(message, options || {});
|
|
@@ -3620,6 +4366,18 @@ class OpenAiProvider {
|
|
|
3620
4366
|
}
|
|
3621
4367
|
return response;
|
|
3622
4368
|
}
|
|
4369
|
+
async *stream(input, options = {}) {
|
|
4370
|
+
const streamLoop = await this.getStreamLoop();
|
|
4371
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4372
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4373
|
+
if (this.conversationHistory.length > 0) {
|
|
4374
|
+
mergedOptions.history = options.history
|
|
4375
|
+
? [...this.conversationHistory, ...options.history]
|
|
4376
|
+
: [...this.conversationHistory];
|
|
4377
|
+
}
|
|
4378
|
+
// Execute stream loop
|
|
4379
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4380
|
+
}
|
|
3623
4381
|
}
|
|
3624
4382
|
|
|
3625
4383
|
// SDK loader with caching
|
|
@@ -3710,6 +4468,17 @@ class OpenRouterProvider {
|
|
|
3710
4468
|
});
|
|
3711
4469
|
return this._operateLoop;
|
|
3712
4470
|
}
|
|
4471
|
+
async getStreamLoop() {
|
|
4472
|
+
if (this._streamLoop) {
|
|
4473
|
+
return this._streamLoop;
|
|
4474
|
+
}
|
|
4475
|
+
const client = await this.getClient();
|
|
4476
|
+
this._streamLoop = createStreamLoop({
|
|
4477
|
+
adapter: openRouterAdapter,
|
|
4478
|
+
client,
|
|
4479
|
+
});
|
|
4480
|
+
return this._streamLoop;
|
|
4481
|
+
}
|
|
3713
4482
|
async send(message, options) {
|
|
3714
4483
|
const client = await this.getClient();
|
|
3715
4484
|
const messages = prepareMessages(message, options);
|
|
@@ -3759,6 +4528,18 @@ class OpenRouterProvider {
|
|
|
3759
4528
|
}
|
|
3760
4529
|
return response;
|
|
3761
4530
|
}
|
|
4531
|
+
async *stream(input, options = {}) {
|
|
4532
|
+
const streamLoop = await this.getStreamLoop();
|
|
4533
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4534
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4535
|
+
if (this.conversationHistory.length > 0) {
|
|
4536
|
+
mergedOptions.history = options.history
|
|
4537
|
+
? [...this.conversationHistory, ...options.history]
|
|
4538
|
+
: [...this.conversationHistory];
|
|
4539
|
+
}
|
|
4540
|
+
// Execute stream loop
|
|
4541
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4542
|
+
}
|
|
3762
4543
|
}
|
|
3763
4544
|
|
|
3764
4545
|
class Llm {
|
|
@@ -3826,6 +4607,12 @@ class Llm {
|
|
|
3826
4607
|
}
|
|
3827
4608
|
return this._llm.operate(input, options);
|
|
3828
4609
|
}
|
|
4610
|
+
async *stream(input, options = {}) {
|
|
4611
|
+
if (!this._llm.stream) {
|
|
4612
|
+
throw new errors.NotImplementedError(`Provider ${this._provider} does not support stream method`);
|
|
4613
|
+
}
|
|
4614
|
+
yield* this._llm.stream(input, options);
|
|
4615
|
+
}
|
|
3829
4616
|
static async send(message, options) {
|
|
3830
4617
|
const { llm, apiKey, model, ...messageOptions } = options || {};
|
|
3831
4618
|
const instance = new Llm(llm, { apiKey, model });
|
|
@@ -3852,6 +4639,27 @@ class Llm {
|
|
|
3852
4639
|
const instance = new Llm(finalLlm, { apiKey, model: finalModel });
|
|
3853
4640
|
return instance.operate(input, operateOptions);
|
|
3854
4641
|
}
|
|
4642
|
+
static stream(input, options) {
|
|
4643
|
+
const { llm, apiKey, model, ...streamOptions } = options || {};
|
|
4644
|
+
let finalLlm = llm;
|
|
4645
|
+
let finalModel = model;
|
|
4646
|
+
if (!llm && model) {
|
|
4647
|
+
const determined = determineModelProvider(model);
|
|
4648
|
+
if (determined.provider) {
|
|
4649
|
+
finalLlm = determined.provider;
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
else if (llm && model) {
|
|
4653
|
+
// When both llm and model are provided, check if they conflict
|
|
4654
|
+
const determined = determineModelProvider(model);
|
|
4655
|
+
if (determined.provider && determined.provider !== llm) {
|
|
4656
|
+
// Don't pass the conflicting model to the constructor
|
|
4657
|
+
finalModel = undefined;
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
const instance = new Llm(finalLlm, { apiKey, model: finalModel });
|
|
4661
|
+
return instance.stream(input, streamOptions);
|
|
4662
|
+
}
|
|
3855
4663
|
}
|
|
3856
4664
|
|
|
3857
4665
|
const random = {
|