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