@jaypie/llm 1.2.0-rc.1 → 1.2.0-rc.10
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 +834 -4
- 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 +835 -5
- 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 +6 -4
package/dist/cjs/index.cjs
CHANGED
|
@@ -158,6 +158,14 @@ function determineModelProvider(input) {
|
|
|
158
158
|
provider: DEFAULT.PROVIDER.NAME,
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
+
// Check for explicit openrouter: prefix
|
|
162
|
+
if (input.startsWith("openrouter:")) {
|
|
163
|
+
const model = input.slice("openrouter:".length);
|
|
164
|
+
return {
|
|
165
|
+
model,
|
|
166
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
161
169
|
// Check if input is a provider name
|
|
162
170
|
if (input === PROVIDER.ANTHROPIC.NAME) {
|
|
163
171
|
return {
|
|
@@ -219,6 +227,14 @@ function determineModelProvider(input) {
|
|
|
219
227
|
};
|
|
220
228
|
}
|
|
221
229
|
}
|
|
230
|
+
// Assume OpenRouter for models containing "/" (e.g., "openai/gpt-4", "anthropic/claude-3-opus")
|
|
231
|
+
// This check must come before match words so that "openai/gpt-4" is not matched by "openai" keyword
|
|
232
|
+
if (input.includes("/")) {
|
|
233
|
+
return {
|
|
234
|
+
model: input,
|
|
235
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
222
238
|
// Check Anthropic match words
|
|
223
239
|
const lowerInput = input.toLowerCase();
|
|
224
240
|
for (const matchWord of PROVIDER.ANTHROPIC.MODEL_MATCH_WORDS) {
|
|
@@ -338,6 +354,19 @@ var LlmResponseStatus;
|
|
|
338
354
|
LlmResponseStatus["InProgress"] = "in_progress";
|
|
339
355
|
})(LlmResponseStatus || (LlmResponseStatus = {}));
|
|
340
356
|
|
|
357
|
+
//
|
|
358
|
+
//
|
|
359
|
+
// Types
|
|
360
|
+
//
|
|
361
|
+
exports.LlmStreamChunkType = void 0;
|
|
362
|
+
(function (LlmStreamChunkType) {
|
|
363
|
+
LlmStreamChunkType["Done"] = "done";
|
|
364
|
+
LlmStreamChunkType["Error"] = "error";
|
|
365
|
+
LlmStreamChunkType["Text"] = "text";
|
|
366
|
+
LlmStreamChunkType["ToolCall"] = "tool_call";
|
|
367
|
+
LlmStreamChunkType["ToolResult"] = "tool_result";
|
|
368
|
+
})(exports.LlmStreamChunkType || (exports.LlmStreamChunkType = {}));
|
|
369
|
+
|
|
341
370
|
/**
|
|
342
371
|
* Converts a string to a standardized LlmInputMessage
|
|
343
372
|
* @param input - String to format
|
|
@@ -722,7 +751,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
722
751
|
//
|
|
723
752
|
buildRequest(request) {
|
|
724
753
|
// Convert messages to Anthropic format (remove 'type' property)
|
|
725
|
-
|
|
754
|
+
// Filter out system messages - Anthropic only accepts system as a top-level field
|
|
755
|
+
const messages = request.messages
|
|
756
|
+
.filter((msg) => {
|
|
757
|
+
const role = msg.role;
|
|
758
|
+
return role !== "system";
|
|
759
|
+
})
|
|
760
|
+
.map((msg) => {
|
|
726
761
|
const anthropicMsg = structuredClone(msg);
|
|
727
762
|
delete anthropicMsg.type;
|
|
728
763
|
return anthropicMsg;
|
|
@@ -815,6 +850,90 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
815
850
|
const anthropic = client;
|
|
816
851
|
return (await anthropic.messages.create(request));
|
|
817
852
|
}
|
|
853
|
+
async *executeStreamRequest(client, request) {
|
|
854
|
+
const anthropic = client;
|
|
855
|
+
const streamRequest = {
|
|
856
|
+
...request,
|
|
857
|
+
stream: true,
|
|
858
|
+
};
|
|
859
|
+
const stream = await anthropic.messages.create(streamRequest);
|
|
860
|
+
// Track current tool call being built
|
|
861
|
+
let currentToolCall = null;
|
|
862
|
+
// Track usage for final chunk
|
|
863
|
+
let inputTokens = 0;
|
|
864
|
+
let outputTokens = 0;
|
|
865
|
+
let model = streamRequest.model;
|
|
866
|
+
for await (const event of stream) {
|
|
867
|
+
if (event.type === "message_start") {
|
|
868
|
+
// Extract initial usage and model info
|
|
869
|
+
const message = event.message;
|
|
870
|
+
if (message.usage) {
|
|
871
|
+
inputTokens = message.usage.input_tokens;
|
|
872
|
+
}
|
|
873
|
+
model = message.model;
|
|
874
|
+
}
|
|
875
|
+
else if (event.type === "content_block_start") {
|
|
876
|
+
const contentBlock = event.content_block;
|
|
877
|
+
if (contentBlock.type === "tool_use") {
|
|
878
|
+
// Start building a tool call
|
|
879
|
+
currentToolCall = {
|
|
880
|
+
id: contentBlock.id,
|
|
881
|
+
name: contentBlock.name,
|
|
882
|
+
arguments: "",
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
else if (event.type === "content_block_delta") {
|
|
887
|
+
const delta = event.delta;
|
|
888
|
+
if (delta.type === "text_delta") {
|
|
889
|
+
yield {
|
|
890
|
+
type: exports.LlmStreamChunkType.Text,
|
|
891
|
+
content: delta.text,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
else if (delta.type === "input_json_delta" && currentToolCall) {
|
|
895
|
+
// Accumulate tool call arguments
|
|
896
|
+
currentToolCall.arguments += delta.partial_json;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
else if (event.type === "content_block_stop") {
|
|
900
|
+
// If we were building a tool call, emit it now
|
|
901
|
+
if (currentToolCall) {
|
|
902
|
+
yield {
|
|
903
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
904
|
+
toolCall: {
|
|
905
|
+
id: currentToolCall.id,
|
|
906
|
+
name: currentToolCall.name,
|
|
907
|
+
arguments: currentToolCall.arguments,
|
|
908
|
+
},
|
|
909
|
+
};
|
|
910
|
+
currentToolCall = null;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
else if (event.type === "message_delta") {
|
|
914
|
+
// Extract final usage
|
|
915
|
+
if (event.usage) {
|
|
916
|
+
outputTokens = event.usage.output_tokens;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
else if (event.type === "message_stop") {
|
|
920
|
+
// Emit done chunk with usage
|
|
921
|
+
yield {
|
|
922
|
+
type: exports.LlmStreamChunkType.Done,
|
|
923
|
+
usage: [
|
|
924
|
+
{
|
|
925
|
+
input: inputTokens,
|
|
926
|
+
output: outputTokens,
|
|
927
|
+
reasoning: 0,
|
|
928
|
+
total: inputTokens + outputTokens,
|
|
929
|
+
provider: this.name,
|
|
930
|
+
model,
|
|
931
|
+
},
|
|
932
|
+
],
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
818
937
|
//
|
|
819
938
|
// Response Parsing
|
|
820
939
|
//
|
|
@@ -1147,6 +1266,77 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1147
1266
|
});
|
|
1148
1267
|
return response;
|
|
1149
1268
|
}
|
|
1269
|
+
async *executeStreamRequest(client, request) {
|
|
1270
|
+
const genAI = client;
|
|
1271
|
+
const geminiRequest = request;
|
|
1272
|
+
// Use generateContentStream for streaming
|
|
1273
|
+
const stream = await genAI.models.generateContentStream({
|
|
1274
|
+
model: geminiRequest.model,
|
|
1275
|
+
contents: geminiRequest.contents,
|
|
1276
|
+
config: geminiRequest.config,
|
|
1277
|
+
});
|
|
1278
|
+
// Track current function call being built
|
|
1279
|
+
let currentFunctionCall = null;
|
|
1280
|
+
// Track usage for final chunk
|
|
1281
|
+
let inputTokens = 0;
|
|
1282
|
+
let outputTokens = 0;
|
|
1283
|
+
let reasoningTokens = 0;
|
|
1284
|
+
const model = geminiRequest.model || this.defaultModel;
|
|
1285
|
+
for await (const chunk of stream) {
|
|
1286
|
+
// Extract text content from the chunk
|
|
1287
|
+
const candidate = chunk.candidates?.[0];
|
|
1288
|
+
if (candidate?.content?.parts) {
|
|
1289
|
+
for (const part of candidate.content.parts) {
|
|
1290
|
+
// Handle text content (excluding thought parts)
|
|
1291
|
+
if (part.text && !part.thought) {
|
|
1292
|
+
yield {
|
|
1293
|
+
type: exports.LlmStreamChunkType.Text,
|
|
1294
|
+
content: part.text,
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
// Handle function calls
|
|
1298
|
+
if (part.functionCall) {
|
|
1299
|
+
const functionCall = part.functionCall;
|
|
1300
|
+
currentFunctionCall = {
|
|
1301
|
+
id: functionCall.id || this.generateCallId(),
|
|
1302
|
+
name: functionCall.name || "",
|
|
1303
|
+
arguments: functionCall.args || {},
|
|
1304
|
+
};
|
|
1305
|
+
// Emit the function call immediately
|
|
1306
|
+
yield {
|
|
1307
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
1308
|
+
toolCall: {
|
|
1309
|
+
id: currentFunctionCall.id,
|
|
1310
|
+
name: currentFunctionCall.name,
|
|
1311
|
+
arguments: JSON.stringify(currentFunctionCall.arguments),
|
|
1312
|
+
},
|
|
1313
|
+
};
|
|
1314
|
+
currentFunctionCall = null;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
// Extract usage metadata if present
|
|
1319
|
+
if (chunk.usageMetadata) {
|
|
1320
|
+
inputTokens = chunk.usageMetadata.promptTokenCount || 0;
|
|
1321
|
+
outputTokens = chunk.usageMetadata.candidatesTokenCount || 0;
|
|
1322
|
+
reasoningTokens = chunk.usageMetadata.thoughtsTokenCount || 0;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
// Emit done chunk with final usage
|
|
1326
|
+
yield {
|
|
1327
|
+
type: exports.LlmStreamChunkType.Done,
|
|
1328
|
+
usage: [
|
|
1329
|
+
{
|
|
1330
|
+
input: inputTokens,
|
|
1331
|
+
output: outputTokens,
|
|
1332
|
+
reasoning: reasoningTokens,
|
|
1333
|
+
total: inputTokens + outputTokens,
|
|
1334
|
+
provider: this.name,
|
|
1335
|
+
model,
|
|
1336
|
+
},
|
|
1337
|
+
],
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1150
1340
|
//
|
|
1151
1341
|
// Response Parsing
|
|
1152
1342
|
//
|
|
@@ -1612,6 +1802,95 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
1612
1802
|
// @ts-expect-error OpenAI SDK types don't match our request format exactly
|
|
1613
1803
|
return await openai.responses.create(request);
|
|
1614
1804
|
}
|
|
1805
|
+
async *executeStreamRequest(client, request) {
|
|
1806
|
+
const openai = client;
|
|
1807
|
+
const baseRequest = request;
|
|
1808
|
+
const streamRequest = {
|
|
1809
|
+
...baseRequest,
|
|
1810
|
+
stream: true,
|
|
1811
|
+
};
|
|
1812
|
+
const stream = await openai.responses.create(streamRequest);
|
|
1813
|
+
// Track current function call being built
|
|
1814
|
+
let currentFunctionCall = null;
|
|
1815
|
+
// Track usage for final chunk
|
|
1816
|
+
let inputTokens = 0;
|
|
1817
|
+
let outputTokens = 0;
|
|
1818
|
+
let reasoningTokens = 0;
|
|
1819
|
+
const model = baseRequest.model || this.defaultModel;
|
|
1820
|
+
// Cast to async iterable - when stream: true, this is always a Stream<ResponseStreamEvent>
|
|
1821
|
+
const asyncStream = stream;
|
|
1822
|
+
for await (const event of asyncStream) {
|
|
1823
|
+
const eventType = event.type;
|
|
1824
|
+
if (eventType === "response.output_text.delta") {
|
|
1825
|
+
// Text content delta
|
|
1826
|
+
const delta = event.delta;
|
|
1827
|
+
if (delta) {
|
|
1828
|
+
yield {
|
|
1829
|
+
type: exports.LlmStreamChunkType.Text,
|
|
1830
|
+
content: delta,
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
else if (eventType === "response.function_call_arguments.delta") {
|
|
1835
|
+
// Function call arguments delta - accumulate
|
|
1836
|
+
const delta = event.delta;
|
|
1837
|
+
if (delta && currentFunctionCall) {
|
|
1838
|
+
currentFunctionCall.arguments += delta;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
else if (eventType === "response.output_item.added") {
|
|
1842
|
+
// New output item - check if it's a function call
|
|
1843
|
+
const item = event.item;
|
|
1844
|
+
if (item?.type === "function_call") {
|
|
1845
|
+
currentFunctionCall = {
|
|
1846
|
+
id: item.id || "",
|
|
1847
|
+
callId: item.call_id || "",
|
|
1848
|
+
name: item.name || "",
|
|
1849
|
+
arguments: "",
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
else if (eventType === "response.output_item.done") {
|
|
1854
|
+
// Output item completed - emit function call if that's what we were building
|
|
1855
|
+
if (currentFunctionCall) {
|
|
1856
|
+
yield {
|
|
1857
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
1858
|
+
toolCall: {
|
|
1859
|
+
id: currentFunctionCall.callId,
|
|
1860
|
+
name: currentFunctionCall.name,
|
|
1861
|
+
arguments: currentFunctionCall.arguments,
|
|
1862
|
+
},
|
|
1863
|
+
};
|
|
1864
|
+
currentFunctionCall = null;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
else if (eventType === "response.completed") {
|
|
1868
|
+
// Response completed - extract final usage
|
|
1869
|
+
const response = event.response;
|
|
1870
|
+
if (response?.usage) {
|
|
1871
|
+
inputTokens = response.usage.input_tokens || 0;
|
|
1872
|
+
outputTokens = response.usage.output_tokens || 0;
|
|
1873
|
+
reasoningTokens = response.usage.output_tokens_details?.reasoning_tokens || 0;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
else if (eventType === "response.done") {
|
|
1877
|
+
// Stream done - emit final chunk with usage
|
|
1878
|
+
yield {
|
|
1879
|
+
type: exports.LlmStreamChunkType.Done,
|
|
1880
|
+
usage: [
|
|
1881
|
+
{
|
|
1882
|
+
input: inputTokens,
|
|
1883
|
+
output: outputTokens,
|
|
1884
|
+
reasoning: reasoningTokens,
|
|
1885
|
+
total: inputTokens + outputTokens,
|
|
1886
|
+
provider: this.name,
|
|
1887
|
+
model,
|
|
1888
|
+
},
|
|
1889
|
+
],
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1615
1894
|
//
|
|
1616
1895
|
// Response Parsing
|
|
1617
1896
|
//
|
|
@@ -1924,6 +2203,110 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
1924
2203
|
});
|
|
1925
2204
|
return response;
|
|
1926
2205
|
}
|
|
2206
|
+
async *executeStreamRequest(client, request) {
|
|
2207
|
+
const openRouter = client;
|
|
2208
|
+
const openRouterRequest = request;
|
|
2209
|
+
// Use chat.send with stream: true for streaming responses
|
|
2210
|
+
const stream = await openRouter.chat.send({
|
|
2211
|
+
model: openRouterRequest.model,
|
|
2212
|
+
messages: openRouterRequest.messages,
|
|
2213
|
+
tools: openRouterRequest.tools,
|
|
2214
|
+
toolChoice: openRouterRequest.tool_choice,
|
|
2215
|
+
user: openRouterRequest.user,
|
|
2216
|
+
stream: true,
|
|
2217
|
+
});
|
|
2218
|
+
// Track current tool call being built
|
|
2219
|
+
let currentToolCall = null;
|
|
2220
|
+
// Track usage for final chunk
|
|
2221
|
+
let inputTokens = 0;
|
|
2222
|
+
let outputTokens = 0;
|
|
2223
|
+
const model = openRouterRequest.model || this.defaultModel;
|
|
2224
|
+
for await (const chunk of stream) {
|
|
2225
|
+
const typedChunk = chunk;
|
|
2226
|
+
const choices = typedChunk.choices;
|
|
2227
|
+
if (choices && choices.length > 0) {
|
|
2228
|
+
const delta = choices[0].delta;
|
|
2229
|
+
// Handle text content
|
|
2230
|
+
if (delta?.content) {
|
|
2231
|
+
yield {
|
|
2232
|
+
type: exports.LlmStreamChunkType.Text,
|
|
2233
|
+
content: delta.content,
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
// Handle tool calls
|
|
2237
|
+
if (delta?.tool_calls && delta.tool_calls.length > 0) {
|
|
2238
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
2239
|
+
if (toolCallDelta.id) {
|
|
2240
|
+
// New tool call starting
|
|
2241
|
+
if (currentToolCall) {
|
|
2242
|
+
// Emit the previous tool call
|
|
2243
|
+
yield {
|
|
2244
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
2245
|
+
toolCall: {
|
|
2246
|
+
id: currentToolCall.id,
|
|
2247
|
+
name: currentToolCall.name,
|
|
2248
|
+
arguments: currentToolCall.arguments,
|
|
2249
|
+
},
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
currentToolCall = {
|
|
2253
|
+
id: toolCallDelta.id,
|
|
2254
|
+
name: toolCallDelta.function?.name || "",
|
|
2255
|
+
arguments: toolCallDelta.function?.arguments || "",
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
else if (currentToolCall) {
|
|
2259
|
+
// Continuing existing tool call
|
|
2260
|
+
if (toolCallDelta.function?.name) {
|
|
2261
|
+
currentToolCall.name += toolCallDelta.function.name;
|
|
2262
|
+
}
|
|
2263
|
+
if (toolCallDelta.function?.arguments) {
|
|
2264
|
+
currentToolCall.arguments += toolCallDelta.function.arguments;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
// Check for finish reason
|
|
2270
|
+
if (choices[0].finish_reason) {
|
|
2271
|
+
// Emit any pending tool call
|
|
2272
|
+
if (currentToolCall) {
|
|
2273
|
+
yield {
|
|
2274
|
+
type: exports.LlmStreamChunkType.ToolCall,
|
|
2275
|
+
toolCall: {
|
|
2276
|
+
id: currentToolCall.id,
|
|
2277
|
+
name: currentToolCall.name,
|
|
2278
|
+
arguments: currentToolCall.arguments,
|
|
2279
|
+
},
|
|
2280
|
+
};
|
|
2281
|
+
currentToolCall = null;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
// Extract usage if present (usually in the final chunk)
|
|
2286
|
+
if (typedChunk.usage) {
|
|
2287
|
+
inputTokens =
|
|
2288
|
+
typedChunk.usage.prompt_tokens || typedChunk.usage.promptTokens || 0;
|
|
2289
|
+
outputTokens =
|
|
2290
|
+
typedChunk.usage.completion_tokens ||
|
|
2291
|
+
typedChunk.usage.completionTokens ||
|
|
2292
|
+
0;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
// Emit done chunk with final usage
|
|
2296
|
+
yield {
|
|
2297
|
+
type: exports.LlmStreamChunkType.Done,
|
|
2298
|
+
usage: [
|
|
2299
|
+
{
|
|
2300
|
+
input: inputTokens,
|
|
2301
|
+
output: outputTokens,
|
|
2302
|
+
reasoning: 0,
|
|
2303
|
+
total: inputTokens + outputTokens,
|
|
2304
|
+
provider: this.name,
|
|
2305
|
+
model,
|
|
2306
|
+
},
|
|
2307
|
+
],
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
1927
2310
|
//
|
|
1928
2311
|
// Response Parsing
|
|
1929
2312
|
//
|
|
@@ -2771,7 +3154,7 @@ class RetryExecutor {
|
|
|
2771
3154
|
//
|
|
2772
3155
|
// Constants
|
|
2773
3156
|
//
|
|
2774
|
-
const ERROR = {
|
|
3157
|
+
const ERROR$1 = {
|
|
2775
3158
|
BAD_FUNCTION_CALL: "Bad Function Call",
|
|
2776
3159
|
};
|
|
2777
3160
|
//
|
|
@@ -2808,13 +3191,16 @@ class OperateLoop {
|
|
|
2808
3191
|
this.client = config.client;
|
|
2809
3192
|
this.hookRunnerInstance = config.hookRunner ?? hookRunner;
|
|
2810
3193
|
this.inputProcessorInstance = config.inputProcessor ?? inputProcessor;
|
|
2811
|
-
this.maxRetries = config.maxRetries ?? 6;
|
|
2812
3194
|
this.retryPolicy = config.retryPolicy ?? defaultRetryPolicy;
|
|
2813
3195
|
}
|
|
2814
3196
|
/**
|
|
2815
3197
|
* Execute the operate loop for multi-turn conversations with tool calling.
|
|
2816
3198
|
*/
|
|
2817
3199
|
async execute(input, options = {}) {
|
|
3200
|
+
// Log what was passed to operate
|
|
3201
|
+
log$1.trace("[operate] Starting operate loop");
|
|
3202
|
+
log$1.var({ "operate.input": input });
|
|
3203
|
+
log$1.var({ "operate.options": options });
|
|
2818
3204
|
// Initialize state
|
|
2819
3205
|
const state = this.initializeState(input, options);
|
|
2820
3206
|
const context = this.createContext(options);
|
|
@@ -2917,6 +3303,9 @@ class OperateLoop {
|
|
|
2917
3303
|
});
|
|
2918
3304
|
// Build provider-specific request
|
|
2919
3305
|
const providerRequest = this.adapter.buildRequest(request);
|
|
3306
|
+
// Log what was passed to the model
|
|
3307
|
+
log$1.trace("[operate] Calling model");
|
|
3308
|
+
log$1.var({ "operate.request": providerRequest });
|
|
2920
3309
|
// Execute beforeEachModelRequest hook
|
|
2921
3310
|
await this.hookRunnerInstance.runBeforeModelRequest(context.hooks, {
|
|
2922
3311
|
input: state.currentInput,
|
|
@@ -2932,6 +3321,9 @@ class OperateLoop {
|
|
|
2932
3321
|
},
|
|
2933
3322
|
hooks: context.hooks,
|
|
2934
3323
|
});
|
|
3324
|
+
// Log what was returned from the model
|
|
3325
|
+
log$1.trace("[operate] Model response received");
|
|
3326
|
+
log$1.var({ "operate.response": response });
|
|
2935
3327
|
// Parse response
|
|
2936
3328
|
const parsed = this.adapter.parseResponse(response, options);
|
|
2937
3329
|
// Track usage
|
|
@@ -3020,7 +3412,7 @@ class OperateLoop {
|
|
|
3020
3412
|
state.responseBuilder.setError({
|
|
3021
3413
|
detail,
|
|
3022
3414
|
status: jaypieError.status,
|
|
3023
|
-
title: ERROR.BAD_FUNCTION_CALL,
|
|
3415
|
+
title: ERROR$1.BAD_FUNCTION_CALL,
|
|
3024
3416
|
});
|
|
3025
3417
|
log$1.error(`Error executing function call ${toolCall.name}`);
|
|
3026
3418
|
log$1.var({ error });
|
|
@@ -3151,6 +3543,325 @@ function createOperateLoop(config) {
|
|
|
3151
3543
|
return new OperateLoop(config);
|
|
3152
3544
|
}
|
|
3153
3545
|
|
|
3546
|
+
//
|
|
3547
|
+
//
|
|
3548
|
+
// Constants
|
|
3549
|
+
//
|
|
3550
|
+
const ERROR = {
|
|
3551
|
+
BAD_FUNCTION_CALL: "Bad Function Call",
|
|
3552
|
+
};
|
|
3553
|
+
//
|
|
3554
|
+
//
|
|
3555
|
+
// Main
|
|
3556
|
+
//
|
|
3557
|
+
/**
|
|
3558
|
+
* StreamLoop implements streaming multi-turn conversation loop.
|
|
3559
|
+
* It orchestrates provider adapters and tool calling while yielding
|
|
3560
|
+
* stream chunks as they become available.
|
|
3561
|
+
*/
|
|
3562
|
+
class StreamLoop {
|
|
3563
|
+
constructor(config) {
|
|
3564
|
+
this.adapter = config.adapter;
|
|
3565
|
+
this.client = config.client;
|
|
3566
|
+
this.hookRunnerInstance = config.hookRunner ?? hookRunner;
|
|
3567
|
+
this.inputProcessorInstance = config.inputProcessor ?? inputProcessor;
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* Execute the streaming loop for multi-turn conversations with tool calling.
|
|
3571
|
+
* Yields stream chunks as they become available.
|
|
3572
|
+
*/
|
|
3573
|
+
async *execute(input, options = {}) {
|
|
3574
|
+
// Verify adapter supports streaming
|
|
3575
|
+
if (!this.adapter.executeStreamRequest) {
|
|
3576
|
+
throw new errors.BadGatewayError(`Provider ${this.adapter.name} does not support streaming`);
|
|
3577
|
+
}
|
|
3578
|
+
// Initialize state
|
|
3579
|
+
const state = this.initializeState(input, options);
|
|
3580
|
+
const context = this.createContext(options);
|
|
3581
|
+
// Build initial request
|
|
3582
|
+
let request = this.buildInitialRequest(state, options);
|
|
3583
|
+
// Multi-turn loop
|
|
3584
|
+
while (state.currentTurn < state.maxTurns) {
|
|
3585
|
+
state.currentTurn++;
|
|
3586
|
+
// Execute one streaming turn
|
|
3587
|
+
const { shouldContinue, toolCalls } = yield* this.executeOneStreamingTurn(request, state, context, options);
|
|
3588
|
+
if (!shouldContinue) {
|
|
3589
|
+
break;
|
|
3590
|
+
}
|
|
3591
|
+
// If we have tool calls, process them
|
|
3592
|
+
if (toolCalls && toolCalls.length > 0 && state.toolkit) {
|
|
3593
|
+
yield* this.processToolCalls(toolCalls, state, context, options);
|
|
3594
|
+
// Check if we've reached max turns
|
|
3595
|
+
if (state.currentTurn >= state.maxTurns) {
|
|
3596
|
+
const error = new errors.TooManyRequestsError();
|
|
3597
|
+
const detail = `Model requested function call but exceeded ${state.maxTurns} turns`;
|
|
3598
|
+
log$1.warn(detail);
|
|
3599
|
+
yield {
|
|
3600
|
+
type: exports.LlmStreamChunkType.Error,
|
|
3601
|
+
error: {
|
|
3602
|
+
detail,
|
|
3603
|
+
status: error.status,
|
|
3604
|
+
title: error.title,
|
|
3605
|
+
},
|
|
3606
|
+
};
|
|
3607
|
+
break;
|
|
3608
|
+
}
|
|
3609
|
+
// Rebuild request with updated history for next turn
|
|
3610
|
+
request = {
|
|
3611
|
+
format: state.formattedFormat,
|
|
3612
|
+
instructions: options.instructions,
|
|
3613
|
+
messages: state.currentInput,
|
|
3614
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
3615
|
+
providerOptions: options.providerOptions,
|
|
3616
|
+
system: options.system,
|
|
3617
|
+
tools: state.formattedTools,
|
|
3618
|
+
user: options.user,
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
else {
|
|
3622
|
+
break;
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
// Emit final done chunk with accumulated usage
|
|
3626
|
+
yield {
|
|
3627
|
+
type: exports.LlmStreamChunkType.Done,
|
|
3628
|
+
usage: state.usageItems,
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
//
|
|
3632
|
+
// Private Methods
|
|
3633
|
+
//
|
|
3634
|
+
initializeState(input, options) {
|
|
3635
|
+
// Process input with placeholders
|
|
3636
|
+
const processedInput = this.inputProcessorInstance.process(input, options);
|
|
3637
|
+
// Determine max turns
|
|
3638
|
+
const maxTurns = maxTurnsFromOptions(options);
|
|
3639
|
+
// Get toolkit
|
|
3640
|
+
let toolkit;
|
|
3641
|
+
if (options.tools) {
|
|
3642
|
+
if (options.tools instanceof Toolkit) {
|
|
3643
|
+
toolkit = options.tools;
|
|
3644
|
+
}
|
|
3645
|
+
else if (Array.isArray(options.tools) && options.tools.length > 0) {
|
|
3646
|
+
const explain = options.explain ?? false;
|
|
3647
|
+
toolkit = new Toolkit(options.tools, { explain });
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
// Format output schema through adapter if provided
|
|
3651
|
+
let formattedFormat;
|
|
3652
|
+
if (options.format) {
|
|
3653
|
+
formattedFormat = this.adapter.formatOutputSchema(options.format);
|
|
3654
|
+
}
|
|
3655
|
+
// Format tools through adapter
|
|
3656
|
+
const formattedTools = toolkit
|
|
3657
|
+
? this.adapter.formatTools(toolkit, formattedFormat)
|
|
3658
|
+
: undefined;
|
|
3659
|
+
return {
|
|
3660
|
+
currentInput: processedInput.history,
|
|
3661
|
+
currentTurn: 0,
|
|
3662
|
+
formattedFormat,
|
|
3663
|
+
formattedTools,
|
|
3664
|
+
maxTurns,
|
|
3665
|
+
toolkit,
|
|
3666
|
+
usageItems: [],
|
|
3667
|
+
};
|
|
3668
|
+
}
|
|
3669
|
+
createContext(options) {
|
|
3670
|
+
return {
|
|
3671
|
+
hooks: options.hooks ?? {},
|
|
3672
|
+
options,
|
|
3673
|
+
};
|
|
3674
|
+
}
|
|
3675
|
+
buildInitialRequest(state, options) {
|
|
3676
|
+
return {
|
|
3677
|
+
format: state.formattedFormat,
|
|
3678
|
+
instructions: options.instructions,
|
|
3679
|
+
messages: state.currentInput,
|
|
3680
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
3681
|
+
providerOptions: options.providerOptions,
|
|
3682
|
+
system: options.system,
|
|
3683
|
+
tools: state.formattedTools,
|
|
3684
|
+
user: options.user,
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
async *executeOneStreamingTurn(request, state, context, options) {
|
|
3688
|
+
// Build provider-specific request
|
|
3689
|
+
const providerRequest = this.adapter.buildRequest(request);
|
|
3690
|
+
// Execute beforeEachModelRequest hook
|
|
3691
|
+
await this.hookRunnerInstance.runBeforeModelRequest(context.hooks, {
|
|
3692
|
+
input: state.currentInput,
|
|
3693
|
+
options,
|
|
3694
|
+
providerRequest,
|
|
3695
|
+
});
|
|
3696
|
+
// Collect tool calls from the stream
|
|
3697
|
+
const collectedToolCalls = [];
|
|
3698
|
+
// Execute streaming request
|
|
3699
|
+
const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest);
|
|
3700
|
+
for await (const chunk of streamGenerator) {
|
|
3701
|
+
// Pass through text chunks
|
|
3702
|
+
if (chunk.type === exports.LlmStreamChunkType.Text) {
|
|
3703
|
+
yield chunk;
|
|
3704
|
+
}
|
|
3705
|
+
// Collect tool calls
|
|
3706
|
+
if (chunk.type === exports.LlmStreamChunkType.ToolCall) {
|
|
3707
|
+
collectedToolCalls.push({
|
|
3708
|
+
callId: chunk.toolCall.id,
|
|
3709
|
+
name: chunk.toolCall.name,
|
|
3710
|
+
arguments: chunk.toolCall.arguments,
|
|
3711
|
+
raw: chunk.toolCall,
|
|
3712
|
+
});
|
|
3713
|
+
yield chunk;
|
|
3714
|
+
}
|
|
3715
|
+
// Track usage from done chunk (but don't yield it yet - we'll emit our own)
|
|
3716
|
+
if (chunk.type === exports.LlmStreamChunkType.Done && chunk.usage) {
|
|
3717
|
+
state.usageItems.push(...chunk.usage);
|
|
3718
|
+
}
|
|
3719
|
+
// Pass through error chunks
|
|
3720
|
+
if (chunk.type === exports.LlmStreamChunkType.Error) {
|
|
3721
|
+
yield chunk;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
// Execute afterEachModelResponse hook
|
|
3725
|
+
await this.hookRunnerInstance.runAfterModelResponse(context.hooks, {
|
|
3726
|
+
content: "",
|
|
3727
|
+
input: state.currentInput,
|
|
3728
|
+
options,
|
|
3729
|
+
providerRequest,
|
|
3730
|
+
providerResponse: null,
|
|
3731
|
+
usage: state.usageItems,
|
|
3732
|
+
});
|
|
3733
|
+
// If we have tool calls and a toolkit, continue the loop
|
|
3734
|
+
if (collectedToolCalls.length > 0 && state.toolkit && state.maxTurns > 1) {
|
|
3735
|
+
// Add tool calls to history
|
|
3736
|
+
for (const toolCall of collectedToolCalls) {
|
|
3737
|
+
state.currentInput.push({
|
|
3738
|
+
type: exports.LlmMessageType.FunctionCall,
|
|
3739
|
+
name: toolCall.name,
|
|
3740
|
+
arguments: toolCall.arguments,
|
|
3741
|
+
call_id: toolCall.callId,
|
|
3742
|
+
id: toolCall.callId,
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
return { shouldContinue: true, toolCalls: collectedToolCalls };
|
|
3746
|
+
}
|
|
3747
|
+
return { shouldContinue: false };
|
|
3748
|
+
}
|
|
3749
|
+
async *processToolCalls(toolCalls, state, context, _options) {
|
|
3750
|
+
for (const toolCall of toolCalls) {
|
|
3751
|
+
try {
|
|
3752
|
+
// Execute beforeEachTool hook
|
|
3753
|
+
await this.hookRunnerInstance.runBeforeTool(context.hooks, {
|
|
3754
|
+
args: toolCall.arguments,
|
|
3755
|
+
toolName: toolCall.name,
|
|
3756
|
+
});
|
|
3757
|
+
// Call the tool
|
|
3758
|
+
log$1.trace(`[stream] Calling tool - ${toolCall.name}`);
|
|
3759
|
+
const result = await state.toolkit.call({
|
|
3760
|
+
arguments: toolCall.arguments,
|
|
3761
|
+
name: toolCall.name,
|
|
3762
|
+
});
|
|
3763
|
+
// Execute afterEachTool hook
|
|
3764
|
+
await this.hookRunnerInstance.runAfterTool(context.hooks, {
|
|
3765
|
+
args: toolCall.arguments,
|
|
3766
|
+
result,
|
|
3767
|
+
toolName: toolCall.name,
|
|
3768
|
+
});
|
|
3769
|
+
// Yield tool result chunk
|
|
3770
|
+
yield {
|
|
3771
|
+
type: exports.LlmStreamChunkType.ToolResult,
|
|
3772
|
+
toolResult: {
|
|
3773
|
+
id: toolCall.callId,
|
|
3774
|
+
name: toolCall.name,
|
|
3775
|
+
result,
|
|
3776
|
+
},
|
|
3777
|
+
};
|
|
3778
|
+
// Add tool result to history
|
|
3779
|
+
state.currentInput.push({
|
|
3780
|
+
type: exports.LlmMessageType.FunctionCallOutput,
|
|
3781
|
+
output: JSON.stringify(result),
|
|
3782
|
+
call_id: toolCall.callId,
|
|
3783
|
+
name: toolCall.name,
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
catch (error) {
|
|
3787
|
+
// Execute onToolError hook
|
|
3788
|
+
await this.hookRunnerInstance.runOnToolError(context.hooks, {
|
|
3789
|
+
args: toolCall.arguments,
|
|
3790
|
+
error: error,
|
|
3791
|
+
toolName: toolCall.name,
|
|
3792
|
+
});
|
|
3793
|
+
// Yield error chunk
|
|
3794
|
+
const jaypieError = new errors.BadGatewayError();
|
|
3795
|
+
const detail = [
|
|
3796
|
+
`Error executing function call ${toolCall.name}.`,
|
|
3797
|
+
error.message,
|
|
3798
|
+
].join("\n");
|
|
3799
|
+
yield {
|
|
3800
|
+
type: exports.LlmStreamChunkType.Error,
|
|
3801
|
+
error: {
|
|
3802
|
+
detail,
|
|
3803
|
+
status: jaypieError.status,
|
|
3804
|
+
title: ERROR.BAD_FUNCTION_CALL,
|
|
3805
|
+
},
|
|
3806
|
+
};
|
|
3807
|
+
log$1.error(`Error executing function call ${toolCall.name}`);
|
|
3808
|
+
log$1.var({ error });
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
/**
|
|
3813
|
+
* Convert Gemini contents format to internal history format.
|
|
3814
|
+
*/
|
|
3815
|
+
convertGeminiContentsToHistory(contents) {
|
|
3816
|
+
const history = [];
|
|
3817
|
+
for (const content of contents) {
|
|
3818
|
+
if (!content.parts)
|
|
3819
|
+
continue;
|
|
3820
|
+
for (const part of content.parts) {
|
|
3821
|
+
if (part.text && typeof part.text === "string") {
|
|
3822
|
+
history.push({
|
|
3823
|
+
role: content.role === "model"
|
|
3824
|
+
? exports.LlmMessageRole.Assistant
|
|
3825
|
+
: exports.LlmMessageRole.User,
|
|
3826
|
+
content: part.text,
|
|
3827
|
+
type: exports.LlmMessageType.Message,
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
else if (part.functionCall) {
|
|
3831
|
+
const fc = part.functionCall;
|
|
3832
|
+
history.push({
|
|
3833
|
+
type: exports.LlmMessageType.FunctionCall,
|
|
3834
|
+
name: fc.name || "",
|
|
3835
|
+
arguments: JSON.stringify(fc.args || {}),
|
|
3836
|
+
call_id: fc.id || "",
|
|
3837
|
+
id: fc.id || "",
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3840
|
+
else if (part.functionResponse) {
|
|
3841
|
+
const fr = part.functionResponse;
|
|
3842
|
+
history.push({
|
|
3843
|
+
type: exports.LlmMessageType.FunctionCallOutput,
|
|
3844
|
+
output: JSON.stringify(fr.response || {}),
|
|
3845
|
+
call_id: "",
|
|
3846
|
+
name: fr.name || "",
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return history;
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
//
|
|
3855
|
+
//
|
|
3856
|
+
// Factory
|
|
3857
|
+
//
|
|
3858
|
+
/**
|
|
3859
|
+
* Create a StreamLoop instance with the specified configuration.
|
|
3860
|
+
*/
|
|
3861
|
+
function createStreamLoop(config) {
|
|
3862
|
+
return new StreamLoop(config);
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3154
3865
|
// SDK loader with caching
|
|
3155
3866
|
let cachedSdk$2 = null;
|
|
3156
3867
|
async function loadSdk$2() {
|
|
@@ -3306,6 +4017,17 @@ class AnthropicProvider {
|
|
|
3306
4017
|
});
|
|
3307
4018
|
return this._operateLoop;
|
|
3308
4019
|
}
|
|
4020
|
+
async getStreamLoop() {
|
|
4021
|
+
if (this._streamLoop) {
|
|
4022
|
+
return this._streamLoop;
|
|
4023
|
+
}
|
|
4024
|
+
const client = await this.getClient();
|
|
4025
|
+
this._streamLoop = createStreamLoop({
|
|
4026
|
+
adapter: anthropicAdapter,
|
|
4027
|
+
client,
|
|
4028
|
+
});
|
|
4029
|
+
return this._streamLoop;
|
|
4030
|
+
}
|
|
3309
4031
|
// Main send method
|
|
3310
4032
|
async send(message, options) {
|
|
3311
4033
|
const client = await this.getClient();
|
|
@@ -3342,6 +4064,18 @@ class AnthropicProvider {
|
|
|
3342
4064
|
}
|
|
3343
4065
|
return response;
|
|
3344
4066
|
}
|
|
4067
|
+
async *stream(input, options = {}) {
|
|
4068
|
+
const streamLoop = await this.getStreamLoop();
|
|
4069
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4070
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4071
|
+
if (this.conversationHistory.length > 0) {
|
|
4072
|
+
mergedOptions.history = options.history
|
|
4073
|
+
? [...this.conversationHistory, ...options.history]
|
|
4074
|
+
: [...this.conversationHistory];
|
|
4075
|
+
}
|
|
4076
|
+
// Execute stream loop
|
|
4077
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4078
|
+
}
|
|
3345
4079
|
}
|
|
3346
4080
|
|
|
3347
4081
|
// SDK loader with caching
|
|
@@ -3418,6 +4152,17 @@ class GeminiProvider {
|
|
|
3418
4152
|
});
|
|
3419
4153
|
return this._operateLoop;
|
|
3420
4154
|
}
|
|
4155
|
+
async getStreamLoop() {
|
|
4156
|
+
if (this._streamLoop) {
|
|
4157
|
+
return this._streamLoop;
|
|
4158
|
+
}
|
|
4159
|
+
const client = await this.getClient();
|
|
4160
|
+
this._streamLoop = createStreamLoop({
|
|
4161
|
+
adapter: geminiAdapter,
|
|
4162
|
+
client,
|
|
4163
|
+
});
|
|
4164
|
+
return this._streamLoop;
|
|
4165
|
+
}
|
|
3421
4166
|
async send(message, options) {
|
|
3422
4167
|
const client = await this.getClient();
|
|
3423
4168
|
const { messages } = prepareMessages$2(message, options);
|
|
@@ -3472,6 +4217,18 @@ class GeminiProvider {
|
|
|
3472
4217
|
}
|
|
3473
4218
|
return response;
|
|
3474
4219
|
}
|
|
4220
|
+
async *stream(input, options = {}) {
|
|
4221
|
+
const streamLoop = await this.getStreamLoop();
|
|
4222
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4223
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4224
|
+
if (this.conversationHistory.length > 0) {
|
|
4225
|
+
mergedOptions.history = options.history
|
|
4226
|
+
? [...this.conversationHistory, ...options.history]
|
|
4227
|
+
: [...this.conversationHistory];
|
|
4228
|
+
}
|
|
4229
|
+
// Execute stream loop
|
|
4230
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4231
|
+
}
|
|
3475
4232
|
}
|
|
3476
4233
|
|
|
3477
4234
|
// Logger
|
|
@@ -3586,6 +4343,17 @@ class OpenAiProvider {
|
|
|
3586
4343
|
});
|
|
3587
4344
|
return this._operateLoop;
|
|
3588
4345
|
}
|
|
4346
|
+
async getStreamLoop() {
|
|
4347
|
+
if (this._streamLoop) {
|
|
4348
|
+
return this._streamLoop;
|
|
4349
|
+
}
|
|
4350
|
+
const client = await this.getClient();
|
|
4351
|
+
this._streamLoop = createStreamLoop({
|
|
4352
|
+
adapter: openAiAdapter,
|
|
4353
|
+
client,
|
|
4354
|
+
});
|
|
4355
|
+
return this._streamLoop;
|
|
4356
|
+
}
|
|
3589
4357
|
async send(message, options) {
|
|
3590
4358
|
const client = await this.getClient();
|
|
3591
4359
|
const messages = prepareMessages$1(message, options || {});
|
|
@@ -3620,6 +4388,18 @@ class OpenAiProvider {
|
|
|
3620
4388
|
}
|
|
3621
4389
|
return response;
|
|
3622
4390
|
}
|
|
4391
|
+
async *stream(input, options = {}) {
|
|
4392
|
+
const streamLoop = await this.getStreamLoop();
|
|
4393
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4394
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4395
|
+
if (this.conversationHistory.length > 0) {
|
|
4396
|
+
mergedOptions.history = options.history
|
|
4397
|
+
? [...this.conversationHistory, ...options.history]
|
|
4398
|
+
: [...this.conversationHistory];
|
|
4399
|
+
}
|
|
4400
|
+
// Execute stream loop
|
|
4401
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4402
|
+
}
|
|
3623
4403
|
}
|
|
3624
4404
|
|
|
3625
4405
|
// SDK loader with caching
|
|
@@ -3710,6 +4490,17 @@ class OpenRouterProvider {
|
|
|
3710
4490
|
});
|
|
3711
4491
|
return this._operateLoop;
|
|
3712
4492
|
}
|
|
4493
|
+
async getStreamLoop() {
|
|
4494
|
+
if (this._streamLoop) {
|
|
4495
|
+
return this._streamLoop;
|
|
4496
|
+
}
|
|
4497
|
+
const client = await this.getClient();
|
|
4498
|
+
this._streamLoop = createStreamLoop({
|
|
4499
|
+
adapter: openRouterAdapter,
|
|
4500
|
+
client,
|
|
4501
|
+
});
|
|
4502
|
+
return this._streamLoop;
|
|
4503
|
+
}
|
|
3713
4504
|
async send(message, options) {
|
|
3714
4505
|
const client = await this.getClient();
|
|
3715
4506
|
const messages = prepareMessages(message, options);
|
|
@@ -3759,6 +4550,18 @@ class OpenRouterProvider {
|
|
|
3759
4550
|
}
|
|
3760
4551
|
return response;
|
|
3761
4552
|
}
|
|
4553
|
+
async *stream(input, options = {}) {
|
|
4554
|
+
const streamLoop = await this.getStreamLoop();
|
|
4555
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
4556
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
4557
|
+
if (this.conversationHistory.length > 0) {
|
|
4558
|
+
mergedOptions.history = options.history
|
|
4559
|
+
? [...this.conversationHistory, ...options.history]
|
|
4560
|
+
: [...this.conversationHistory];
|
|
4561
|
+
}
|
|
4562
|
+
// Execute stream loop
|
|
4563
|
+
yield* streamLoop.execute(input, mergedOptions);
|
|
4564
|
+
}
|
|
3762
4565
|
}
|
|
3763
4566
|
|
|
3764
4567
|
class Llm {
|
|
@@ -3826,6 +4629,12 @@ class Llm {
|
|
|
3826
4629
|
}
|
|
3827
4630
|
return this._llm.operate(input, options);
|
|
3828
4631
|
}
|
|
4632
|
+
async *stream(input, options = {}) {
|
|
4633
|
+
if (!this._llm.stream) {
|
|
4634
|
+
throw new errors.NotImplementedError(`Provider ${this._provider} does not support stream method`);
|
|
4635
|
+
}
|
|
4636
|
+
yield* this._llm.stream(input, options);
|
|
4637
|
+
}
|
|
3829
4638
|
static async send(message, options) {
|
|
3830
4639
|
const { llm, apiKey, model, ...messageOptions } = options || {};
|
|
3831
4640
|
const instance = new Llm(llm, { apiKey, model });
|
|
@@ -3852,6 +4661,27 @@ class Llm {
|
|
|
3852
4661
|
const instance = new Llm(finalLlm, { apiKey, model: finalModel });
|
|
3853
4662
|
return instance.operate(input, operateOptions);
|
|
3854
4663
|
}
|
|
4664
|
+
static stream(input, options) {
|
|
4665
|
+
const { llm, apiKey, model, ...streamOptions } = options || {};
|
|
4666
|
+
let finalLlm = llm;
|
|
4667
|
+
let finalModel = model;
|
|
4668
|
+
if (!llm && model) {
|
|
4669
|
+
const determined = determineModelProvider(model);
|
|
4670
|
+
if (determined.provider) {
|
|
4671
|
+
finalLlm = determined.provider;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
else if (llm && model) {
|
|
4675
|
+
// When both llm and model are provided, check if they conflict
|
|
4676
|
+
const determined = determineModelProvider(model);
|
|
4677
|
+
if (determined.provider && determined.provider !== llm) {
|
|
4678
|
+
// Don't pass the conflicting model to the constructor
|
|
4679
|
+
finalModel = undefined;
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
const instance = new Llm(finalLlm, { apiKey, model: finalModel });
|
|
4683
|
+
return instance.stream(input, streamOptions);
|
|
4684
|
+
}
|
|
3855
4685
|
}
|
|
3856
4686
|
|
|
3857
4687
|
const random = {
|