llmist 15.18.1 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +306 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -4
- package/dist/index.d.ts +51 -4
- package/dist/index.js +306 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -360,7 +360,8 @@ var init_execution_tree = __esm({
|
|
|
360
360
|
result: params.result ?? "",
|
|
361
361
|
executionTimeMs: params.executionTimeMs ?? 0,
|
|
362
362
|
cost: params.cost,
|
|
363
|
-
media: params.media
|
|
363
|
+
media: params.media,
|
|
364
|
+
storedMedia: params.storedMedia
|
|
364
365
|
});
|
|
365
366
|
}
|
|
366
367
|
}
|
|
@@ -1278,8 +1279,9 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
1278
1279
|
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
1279
1280
|
* @param media - Optional media outputs from the gadget
|
|
1280
1281
|
* @param mediaIds - Optional IDs for the media outputs
|
|
1282
|
+
* @param storedMedia - Optional stored media info including file paths
|
|
1281
1283
|
*/
|
|
1282
|
-
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
|
|
1284
|
+
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds, storedMedia) {
|
|
1283
1285
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
1284
1286
|
this.messages.push({
|
|
1285
1287
|
role: "assistant",
|
|
@@ -1288,7 +1290,11 @@ ${paramStr}
|
|
|
1288
1290
|
${this.endPrefix}`
|
|
1289
1291
|
});
|
|
1290
1292
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
1291
|
-
const idRefs = media.map((m, i) =>
|
|
1293
|
+
const idRefs = media.map((m, i) => {
|
|
1294
|
+
const path = storedMedia?.[i]?.path;
|
|
1295
|
+
const pathInfo = path ? ` \u2192 saved to: ${path}` : "";
|
|
1296
|
+
return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
|
|
1297
|
+
}).join("\n");
|
|
1292
1298
|
const textWithIds = `Result (${invocationId}): ${result}
|
|
1293
1299
|
${idRefs}`;
|
|
1294
1300
|
const parts = [text(textWithIds)];
|
|
@@ -2347,7 +2353,7 @@ function createLogger(options = {}) {
|
|
|
2347
2353
|
}
|
|
2348
2354
|
}
|
|
2349
2355
|
const useFileLogging = Boolean(sharedLogFileStream);
|
|
2350
|
-
const
|
|
2356
|
+
const logger2 = new import_tslog.Logger({
|
|
2351
2357
|
name,
|
|
2352
2358
|
minLevel,
|
|
2353
2359
|
type: useFileLogging ? "pretty" : defaultType,
|
|
@@ -2373,7 +2379,7 @@ function createLogger(options = {}) {
|
|
|
2373
2379
|
}
|
|
2374
2380
|
} : void 0
|
|
2375
2381
|
});
|
|
2376
|
-
return
|
|
2382
|
+
return logger2;
|
|
2377
2383
|
}
|
|
2378
2384
|
var import_node_fs, import_node_path2, import_tslog, LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
|
|
2379
2385
|
var init_logger = __esm({
|
|
@@ -2765,6 +2771,17 @@ var init_gadget = __esm({
|
|
|
2765
2771
|
* ```
|
|
2766
2772
|
*/
|
|
2767
2773
|
maxConcurrent;
|
|
2774
|
+
/**
|
|
2775
|
+
* If true, this gadget must execute alone — no other gadgets in the same
|
|
2776
|
+
* LLM response can run in parallel. When an exclusive gadget arrives and
|
|
2777
|
+
* other gadgets are already in-flight, it is deferred until they complete.
|
|
2778
|
+
*
|
|
2779
|
+
* Use for gadgets that terminate the agent loop (e.g., Finish), where
|
|
2780
|
+
* sibling tool results must be visible to the LLM before the loop ends.
|
|
2781
|
+
*
|
|
2782
|
+
* This is a safety floor: external config cannot weaken it.
|
|
2783
|
+
*/
|
|
2784
|
+
exclusive;
|
|
2768
2785
|
/**
|
|
2769
2786
|
* Throws an AbortException if the execution has been aborted.
|
|
2770
2787
|
*
|
|
@@ -3601,14 +3618,15 @@ var init_conversation_manager = __esm({
|
|
|
3601
3618
|
addAssistantMessage(content) {
|
|
3602
3619
|
this.historyBuilder.addAssistant(content);
|
|
3603
3620
|
}
|
|
3604
|
-
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
|
|
3621
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds, storedMedia) {
|
|
3605
3622
|
this.historyBuilder.addGadgetCallResult(
|
|
3606
3623
|
gadgetName,
|
|
3607
3624
|
parameters,
|
|
3608
3625
|
result,
|
|
3609
3626
|
invocationId,
|
|
3610
3627
|
media,
|
|
3611
|
-
mediaIds
|
|
3628
|
+
mediaIds,
|
|
3629
|
+
storedMedia
|
|
3612
3630
|
);
|
|
3613
3631
|
}
|
|
3614
3632
|
getMessages() {
|
|
@@ -9638,6 +9656,106 @@ var init_openrouter_models = __esm({
|
|
|
9638
9656
|
}
|
|
9639
9657
|
});
|
|
9640
9658
|
|
|
9659
|
+
// src/providers/openrouter-speech-models.ts
|
|
9660
|
+
function getOpenRouterSpeechModelSpec(modelId) {
|
|
9661
|
+
return openrouterSpeechModels.find((m) => m.modelId === modelId);
|
|
9662
|
+
}
|
|
9663
|
+
function isOpenRouterSpeechModel(modelId) {
|
|
9664
|
+
return openrouterSpeechModels.some((m) => m.modelId === modelId);
|
|
9665
|
+
}
|
|
9666
|
+
function calculateOpenRouterSpeechCost(modelId, characterCount, estimatedMinutes) {
|
|
9667
|
+
const spec = getOpenRouterSpeechModelSpec(modelId);
|
|
9668
|
+
if (!spec) return void 0;
|
|
9669
|
+
if (spec.pricing.perMinute !== void 0) {
|
|
9670
|
+
const minutes = estimatedMinutes ?? characterCount / 750;
|
|
9671
|
+
return minutes * spec.pricing.perMinute;
|
|
9672
|
+
}
|
|
9673
|
+
if (spec.pricing.perInputToken !== void 0) {
|
|
9674
|
+
const estimatedTokens = Math.ceil(characterCount / 4);
|
|
9675
|
+
const inputCost = estimatedTokens * spec.pricing.perInputToken;
|
|
9676
|
+
if (spec.pricing.perAudioOutputToken !== void 0 && estimatedMinutes !== void 0) {
|
|
9677
|
+
const audioTokens = estimatedMinutes * 60 * 100;
|
|
9678
|
+
return inputCost + audioTokens * spec.pricing.perAudioOutputToken;
|
|
9679
|
+
}
|
|
9680
|
+
return inputCost;
|
|
9681
|
+
}
|
|
9682
|
+
return void 0;
|
|
9683
|
+
}
|
|
9684
|
+
var OPENROUTER_TTS_VOICES, OPENROUTER_TTS_FORMATS, openrouterSpeechModels;
|
|
9685
|
+
var init_openrouter_speech_models = __esm({
|
|
9686
|
+
"src/providers/openrouter-speech-models.ts"() {
|
|
9687
|
+
"use strict";
|
|
9688
|
+
OPENROUTER_TTS_VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
|
|
9689
|
+
OPENROUTER_TTS_FORMATS = ["pcm16"];
|
|
9690
|
+
openrouterSpeechModels = [
|
|
9691
|
+
{
|
|
9692
|
+
provider: "openrouter",
|
|
9693
|
+
modelId: "openai/gpt-4o-audio-preview",
|
|
9694
|
+
displayName: "GPT-4o Audio Preview (via OpenRouter)",
|
|
9695
|
+
pricing: {
|
|
9696
|
+
perInputToken: 25e-7,
|
|
9697
|
+
perAudioOutputToken: 1e-5,
|
|
9698
|
+
perMinute: 0.06
|
|
9699
|
+
},
|
|
9700
|
+
voices: [...OPENROUTER_TTS_VOICES],
|
|
9701
|
+
formats: OPENROUTER_TTS_FORMATS,
|
|
9702
|
+
maxInputLength: 128e3,
|
|
9703
|
+
defaultVoice: "alloy",
|
|
9704
|
+
defaultFormat: "pcm16",
|
|
9705
|
+
features: {
|
|
9706
|
+
voiceInstructions: true
|
|
9707
|
+
}
|
|
9708
|
+
},
|
|
9709
|
+
{
|
|
9710
|
+
provider: "openrouter",
|
|
9711
|
+
modelId: "openai/gpt-audio",
|
|
9712
|
+
displayName: "GPT Audio (via OpenRouter)",
|
|
9713
|
+
pricing: {
|
|
9714
|
+
// Token-based pricing with audio output
|
|
9715
|
+
perInputToken: 25e-7,
|
|
9716
|
+
// $2.50 per 1M
|
|
9717
|
+
perAudioOutputToken: 64e-6,
|
|
9718
|
+
// ~$64 per 1M audio tokens
|
|
9719
|
+
// Approximate per-minute cost for estimation
|
|
9720
|
+
perMinute: 0.08
|
|
9721
|
+
},
|
|
9722
|
+
voices: [...OPENROUTER_TTS_VOICES],
|
|
9723
|
+
formats: OPENROUTER_TTS_FORMATS,
|
|
9724
|
+
maxInputLength: 128e3,
|
|
9725
|
+
// 128K context
|
|
9726
|
+
defaultVoice: "alloy",
|
|
9727
|
+
defaultFormat: "pcm16",
|
|
9728
|
+
features: {
|
|
9729
|
+
voiceInstructions: true
|
|
9730
|
+
}
|
|
9731
|
+
},
|
|
9732
|
+
{
|
|
9733
|
+
provider: "openrouter",
|
|
9734
|
+
modelId: "openai/gpt-audio-mini",
|
|
9735
|
+
displayName: "GPT Audio Mini (via OpenRouter)",
|
|
9736
|
+
pricing: {
|
|
9737
|
+
// More affordable option
|
|
9738
|
+
perInputToken: 6e-7,
|
|
9739
|
+
// $0.60 per 1M
|
|
9740
|
+
perAudioOutputToken: 6e-7,
|
|
9741
|
+
// $0.60 per 1M audio tokens
|
|
9742
|
+
// Approximate per-minute cost for estimation
|
|
9743
|
+
perMinute: 0.015
|
|
9744
|
+
},
|
|
9745
|
+
voices: [...OPENROUTER_TTS_VOICES],
|
|
9746
|
+
formats: OPENROUTER_TTS_FORMATS,
|
|
9747
|
+
maxInputLength: 128e3,
|
|
9748
|
+
// 128K context
|
|
9749
|
+
defaultVoice: "alloy",
|
|
9750
|
+
defaultFormat: "pcm16",
|
|
9751
|
+
features: {
|
|
9752
|
+
voiceInstructions: true
|
|
9753
|
+
}
|
|
9754
|
+
}
|
|
9755
|
+
];
|
|
9756
|
+
}
|
|
9757
|
+
});
|
|
9758
|
+
|
|
9641
9759
|
// src/providers/openrouter.ts
|
|
9642
9760
|
function createOpenRouterProviderFromEnv() {
|
|
9643
9761
|
const apiKey = readEnvVar("OPENROUTER_API_KEY");
|
|
@@ -9658,14 +9776,18 @@ function createOpenRouterProviderFromEnv() {
|
|
|
9658
9776
|
});
|
|
9659
9777
|
return new OpenRouterProvider(client, config);
|
|
9660
9778
|
}
|
|
9661
|
-
var import_openai4, OPENROUTER_EFFORT_MAP, OpenRouterProvider;
|
|
9779
|
+
var import_openai4, logger, OPENROUTER_EFFORT_MAP, OpenRouterProvider;
|
|
9662
9780
|
var init_openrouter = __esm({
|
|
9663
9781
|
"src/providers/openrouter.ts"() {
|
|
9664
9782
|
"use strict";
|
|
9665
9783
|
import_openai4 = __toESM(require("openai"), 1);
|
|
9784
|
+
init_model_shortcuts();
|
|
9785
|
+
init_logger();
|
|
9666
9786
|
init_openai_compatible_provider();
|
|
9667
9787
|
init_openrouter_models();
|
|
9788
|
+
init_openrouter_speech_models();
|
|
9668
9789
|
init_utils();
|
|
9790
|
+
logger = createLogger({ name: "openrouter" });
|
|
9669
9791
|
OPENROUTER_EFFORT_MAP = {
|
|
9670
9792
|
none: "none",
|
|
9671
9793
|
low: "low",
|
|
@@ -9771,6 +9893,128 @@ Original error: ${error.message}`
|
|
|
9771
9893
|
}
|
|
9772
9894
|
return error;
|
|
9773
9895
|
}
|
|
9896
|
+
// =========================================================================
|
|
9897
|
+
// Speech Generation (TTS via Chat Completions with Audio Modality)
|
|
9898
|
+
// =========================================================================
|
|
9899
|
+
/**
|
|
9900
|
+
* Get speech model specifications for OpenRouter.
|
|
9901
|
+
*/
|
|
9902
|
+
getSpeechModelSpecs() {
|
|
9903
|
+
return openrouterSpeechModels;
|
|
9904
|
+
}
|
|
9905
|
+
/**
|
|
9906
|
+
* Check if this provider supports speech generation for a given model.
|
|
9907
|
+
* Handles both prefixed (openrouter:openai/gpt-audio-mini) and unprefixed model IDs.
|
|
9908
|
+
*/
|
|
9909
|
+
supportsSpeechGeneration(modelId) {
|
|
9910
|
+
const bareModelId = getModelId(modelId);
|
|
9911
|
+
return isOpenRouterSpeechModel(bareModelId);
|
|
9912
|
+
}
|
|
9913
|
+
/**
|
|
9914
|
+
* Generate speech audio from text using OpenRouter's audio-capable models.
|
|
9915
|
+
*
|
|
9916
|
+
* OpenRouter TTS works via chat completions with audio modality, not a
|
|
9917
|
+
* dedicated TTS endpoint. The model receives a prompt asking it to say
|
|
9918
|
+
* the text, and returns audio data via streaming.
|
|
9919
|
+
*
|
|
9920
|
+
* @param options - Speech generation options
|
|
9921
|
+
* @returns Promise resolving to the generation result with audio and cost
|
|
9922
|
+
* @throws Error if model is unknown, voice/format are invalid, or no audio is returned
|
|
9923
|
+
*/
|
|
9924
|
+
async generateSpeech(options) {
|
|
9925
|
+
const client = this.client;
|
|
9926
|
+
const bareModelId = getModelId(options.model);
|
|
9927
|
+
const spec = getOpenRouterSpeechModelSpec(bareModelId);
|
|
9928
|
+
if (!spec) {
|
|
9929
|
+
throw new Error(`Unknown OpenRouter TTS model: ${options.model}`);
|
|
9930
|
+
}
|
|
9931
|
+
const voice = options.voice ?? spec.defaultVoice ?? "alloy";
|
|
9932
|
+
if (!spec.voices.includes(voice)) {
|
|
9933
|
+
throw new Error(
|
|
9934
|
+
`Invalid voice "${voice}" for ${options.model}. Valid voices: ${spec.voices.join(", ")}`
|
|
9935
|
+
);
|
|
9936
|
+
}
|
|
9937
|
+
const format2 = options.responseFormat ?? spec.defaultFormat ?? "mp3";
|
|
9938
|
+
if (!spec.formats.includes(format2)) {
|
|
9939
|
+
throw new Error(
|
|
9940
|
+
`Invalid format "${format2}" for ${options.model}. Valid formats: ${spec.formats.join(", ")}`
|
|
9941
|
+
);
|
|
9942
|
+
}
|
|
9943
|
+
logger.debug("TTS request", {
|
|
9944
|
+
model: bareModelId,
|
|
9945
|
+
voice,
|
|
9946
|
+
format: format2,
|
|
9947
|
+
inputLength: options.input.length
|
|
9948
|
+
});
|
|
9949
|
+
try {
|
|
9950
|
+
const response = await client.chat.completions.create({
|
|
9951
|
+
model: bareModelId,
|
|
9952
|
+
messages: [
|
|
9953
|
+
{
|
|
9954
|
+
role: "user",
|
|
9955
|
+
content: `Please say the following text exactly: "${options.input}"`
|
|
9956
|
+
}
|
|
9957
|
+
],
|
|
9958
|
+
// OpenRouter-specific parameters for audio output
|
|
9959
|
+
// Note: OpenRouter TTS via chat completions does NOT support the speed parameter
|
|
9960
|
+
// (unlike OpenAI's dedicated /audio/speech endpoint which does)
|
|
9961
|
+
modalities: ["text", "audio"],
|
|
9962
|
+
audio: {
|
|
9963
|
+
voice,
|
|
9964
|
+
format: format2
|
|
9965
|
+
},
|
|
9966
|
+
stream: true
|
|
9967
|
+
});
|
|
9968
|
+
const audioChunks = [];
|
|
9969
|
+
for await (const chunk of response) {
|
|
9970
|
+
const delta = chunk.choices[0]?.delta;
|
|
9971
|
+
if (!delta || typeof delta !== "object") continue;
|
|
9972
|
+
const audioObj = delta.audio;
|
|
9973
|
+
if (!audioObj || typeof audioObj !== "object") continue;
|
|
9974
|
+
const audioData = audioObj.data;
|
|
9975
|
+
if (typeof audioData !== "string" || audioData.length === 0) continue;
|
|
9976
|
+
const decoded = Buffer.from(audioData, "base64");
|
|
9977
|
+
if (decoded.length === 0) {
|
|
9978
|
+
throw new Error("Invalid base64 audio data received from OpenRouter");
|
|
9979
|
+
}
|
|
9980
|
+
audioChunks.push(decoded);
|
|
9981
|
+
}
|
|
9982
|
+
const audioBuffer = Buffer.concat(audioChunks);
|
|
9983
|
+
if (audioBuffer.length === 0) {
|
|
9984
|
+
throw new Error(
|
|
9985
|
+
"OpenRouter TTS returned no audio data. The model may have failed to generate audio or the stream was interrupted."
|
|
9986
|
+
);
|
|
9987
|
+
}
|
|
9988
|
+
const cost = calculateOpenRouterSpeechCost(bareModelId, options.input.length);
|
|
9989
|
+
return {
|
|
9990
|
+
// Use Uint8Array for clean ArrayBuffer extraction (safer than buffer.slice for Node.js Buffer)
|
|
9991
|
+
audio: new Uint8Array(audioBuffer).buffer,
|
|
9992
|
+
model: options.model,
|
|
9993
|
+
usage: {
|
|
9994
|
+
characterCount: options.input.length
|
|
9995
|
+
},
|
|
9996
|
+
cost,
|
|
9997
|
+
format: format2
|
|
9998
|
+
};
|
|
9999
|
+
} catch (error) {
|
|
10000
|
+
const err = error;
|
|
10001
|
+
const apiError = err.error?.message || err.error?.code || "";
|
|
10002
|
+
const bodyInfo = err.body ? JSON.stringify(err.body) : "";
|
|
10003
|
+
const details = apiError || bodyInfo || err.message || "Unknown error";
|
|
10004
|
+
logger.debug("TTS error", {
|
|
10005
|
+
model: bareModelId,
|
|
10006
|
+
voice,
|
|
10007
|
+
format: format2,
|
|
10008
|
+
status: err.status,
|
|
10009
|
+
error: err.error,
|
|
10010
|
+
body: err.body,
|
|
10011
|
+
message: err.message
|
|
10012
|
+
});
|
|
10013
|
+
throw new Error(
|
|
10014
|
+
`OpenRouter TTS failed for model ${bareModelId}: ${details}` + (err.status ? ` (HTTP ${err.status})` : "")
|
|
10015
|
+
);
|
|
10016
|
+
}
|
|
10017
|
+
}
|
|
9774
10018
|
};
|
|
9775
10019
|
}
|
|
9776
10020
|
});
|
|
@@ -10749,8 +10993,8 @@ var init_builder = __esm({
|
|
|
10749
10993
|
* @param logger - Logger instance
|
|
10750
10994
|
* @returns This builder for chaining
|
|
10751
10995
|
*/
|
|
10752
|
-
withLogger(
|
|
10753
|
-
this.logger =
|
|
10996
|
+
withLogger(logger2) {
|
|
10997
|
+
this.logger = logger2;
|
|
10754
10998
|
return this;
|
|
10755
10999
|
}
|
|
10756
11000
|
/**
|
|
@@ -12785,6 +13029,7 @@ function Gadget(config) {
|
|
|
12785
13029
|
timeoutMs = config.timeoutMs;
|
|
12786
13030
|
examples = config.examples;
|
|
12787
13031
|
maxConcurrent = config.maxConcurrent;
|
|
13032
|
+
exclusive = config.exclusive;
|
|
12788
13033
|
/**
|
|
12789
13034
|
* Type helper property for accessing inferred parameter type.
|
|
12790
13035
|
* This is used in the execute method signature: `execute(params: this['params'])`
|
|
@@ -12834,7 +13079,7 @@ var init_executor = __esm({
|
|
|
12834
13079
|
init_parser();
|
|
12835
13080
|
init_typed_gadget();
|
|
12836
13081
|
GadgetExecutor = class {
|
|
12837
|
-
constructor(registry, requestHumanInput,
|
|
13082
|
+
constructor(registry, requestHumanInput, logger2, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, tree, parentNodeId, baseDepth, parentObservers, currentObservers, rateLimitTracker, retryConfig) {
|
|
12838
13083
|
this.registry = registry;
|
|
12839
13084
|
this.requestHumanInput = requestHumanInput;
|
|
12840
13085
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -12849,7 +13094,7 @@ var init_executor = __esm({
|
|
|
12849
13094
|
this.currentObservers = currentObservers;
|
|
12850
13095
|
this.rateLimitTracker = rateLimitTracker;
|
|
12851
13096
|
this.retryConfig = retryConfig;
|
|
12852
|
-
this.logger =
|
|
13097
|
+
this.logger = logger2 ?? createLogger({ name: "llmist:executor" });
|
|
12853
13098
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
12854
13099
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
12855
13100
|
}
|
|
@@ -13249,22 +13494,22 @@ function getSubagentContextForNode(tree, nodeId) {
|
|
|
13249
13494
|
depth: node.depth
|
|
13250
13495
|
};
|
|
13251
13496
|
}
|
|
13252
|
-
async function safeObserve(fn,
|
|
13497
|
+
async function safeObserve(fn, logger2, eventType) {
|
|
13253
13498
|
try {
|
|
13254
13499
|
await fn();
|
|
13255
13500
|
} catch (error) {
|
|
13256
|
-
|
|
13501
|
+
logger2.warn(`Observer error in ${eventType}:`, error);
|
|
13257
13502
|
}
|
|
13258
13503
|
}
|
|
13259
|
-
function chainObserverCall(chainMap, key, fn,
|
|
13504
|
+
function chainObserverCall(chainMap, key, fn, logger2, eventType, cleanup = false) {
|
|
13260
13505
|
const previousPromise = chainMap.get(key) ?? Promise.resolve();
|
|
13261
|
-
const newPromise = previousPromise.then(() => safeObserve(fn,
|
|
13506
|
+
const newPromise = previousPromise.then(() => safeObserve(fn, logger2, eventType));
|
|
13262
13507
|
chainMap.set(key, newPromise);
|
|
13263
13508
|
if (cleanup) {
|
|
13264
13509
|
newPromise.finally(() => chainMap.delete(key));
|
|
13265
13510
|
}
|
|
13266
13511
|
}
|
|
13267
|
-
function bridgeTreeToHooks(tree, hooks,
|
|
13512
|
+
function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
13268
13513
|
const gadgetPromiseChains = /* @__PURE__ */ new Map();
|
|
13269
13514
|
const llmPromiseChains = /* @__PURE__ */ new Map();
|
|
13270
13515
|
return tree.onAll((event) => {
|
|
@@ -13286,14 +13531,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13286
13531
|
gadgetName: gadgetEvent.name,
|
|
13287
13532
|
invocationId: gadgetEvent.invocationId,
|
|
13288
13533
|
parameters: gadgetNode?.parameters ?? {},
|
|
13289
|
-
logger,
|
|
13534
|
+
logger: logger2,
|
|
13290
13535
|
subagentContext
|
|
13291
13536
|
};
|
|
13292
13537
|
chainObserverCall(
|
|
13293
13538
|
gadgetPromiseChains,
|
|
13294
13539
|
gadgetEvent.invocationId,
|
|
13295
13540
|
() => hooks.observers?.onGadgetExecutionStart?.(context),
|
|
13296
|
-
|
|
13541
|
+
logger2,
|
|
13297
13542
|
"onGadgetExecutionStart",
|
|
13298
13543
|
false
|
|
13299
13544
|
// Don't cleanup - wait for completion event
|
|
@@ -13313,14 +13558,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13313
13558
|
finalResult: gadgetEvent.result,
|
|
13314
13559
|
executionTimeMs: gadgetEvent.executionTimeMs,
|
|
13315
13560
|
cost: gadgetEvent.cost,
|
|
13316
|
-
logger,
|
|
13561
|
+
logger: logger2,
|
|
13317
13562
|
subagentContext
|
|
13318
13563
|
};
|
|
13319
13564
|
chainObserverCall(
|
|
13320
13565
|
gadgetPromiseChains,
|
|
13321
13566
|
gadgetEvent.invocationId,
|
|
13322
13567
|
() => hooks.observers?.onGadgetExecutionComplete?.(context),
|
|
13323
|
-
|
|
13568
|
+
logger2,
|
|
13324
13569
|
"onGadgetExecutionComplete",
|
|
13325
13570
|
true
|
|
13326
13571
|
// Cleanup after completion
|
|
@@ -13339,14 +13584,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13339
13584
|
parameters: gadgetNode?.parameters ?? {},
|
|
13340
13585
|
error: gadgetEvent.error,
|
|
13341
13586
|
executionTimeMs: gadgetEvent.executionTimeMs,
|
|
13342
|
-
logger,
|
|
13587
|
+
logger: logger2,
|
|
13343
13588
|
subagentContext
|
|
13344
13589
|
};
|
|
13345
13590
|
chainObserverCall(
|
|
13346
13591
|
gadgetPromiseChains,
|
|
13347
13592
|
gadgetEvent.invocationId,
|
|
13348
13593
|
() => hooks.observers?.onGadgetExecutionComplete?.(context),
|
|
13349
|
-
|
|
13594
|
+
logger2,
|
|
13350
13595
|
"onGadgetExecutionComplete",
|
|
13351
13596
|
true
|
|
13352
13597
|
// Cleanup after error
|
|
@@ -13365,14 +13610,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13365
13610
|
parameters: gadgetNode?.parameters ?? {},
|
|
13366
13611
|
failedDependency: gadgetEvent.failedDependency ?? "",
|
|
13367
13612
|
failedDependencyError: gadgetEvent.failedDependencyError ?? gadgetEvent.error,
|
|
13368
|
-
logger,
|
|
13613
|
+
logger: logger2,
|
|
13369
13614
|
subagentContext
|
|
13370
13615
|
};
|
|
13371
13616
|
chainObserverCall(
|
|
13372
13617
|
gadgetPromiseChains,
|
|
13373
13618
|
gadgetEvent.invocationId,
|
|
13374
13619
|
() => hooks.observers?.onGadgetSkipped?.(context),
|
|
13375
|
-
|
|
13620
|
+
logger2,
|
|
13376
13621
|
"onGadgetSkipped",
|
|
13377
13622
|
true
|
|
13378
13623
|
// Cleanup after skipped
|
|
@@ -13397,14 +13642,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13397
13642
|
temperature: void 0,
|
|
13398
13643
|
maxTokens: void 0
|
|
13399
13644
|
},
|
|
13400
|
-
logger,
|
|
13645
|
+
logger: logger2,
|
|
13401
13646
|
subagentContext
|
|
13402
13647
|
};
|
|
13403
13648
|
chainObserverCall(
|
|
13404
13649
|
llmPromiseChains,
|
|
13405
13650
|
event.nodeId,
|
|
13406
13651
|
() => hooks.observers?.onLLMCallStart?.(context),
|
|
13407
|
-
|
|
13652
|
+
logger2,
|
|
13408
13653
|
"onLLMCallStart",
|
|
13409
13654
|
false
|
|
13410
13655
|
// Don't cleanup - wait for completion event
|
|
@@ -13429,14 +13674,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13429
13674
|
rawResponse: llmEvent.response,
|
|
13430
13675
|
// Use rawResponse as finalMessage since interceptor modifications aren't available from tree events
|
|
13431
13676
|
finalMessage: llmEvent.response,
|
|
13432
|
-
logger,
|
|
13677
|
+
logger: logger2,
|
|
13433
13678
|
subagentContext
|
|
13434
13679
|
};
|
|
13435
13680
|
chainObserverCall(
|
|
13436
13681
|
llmPromiseChains,
|
|
13437
13682
|
event.nodeId,
|
|
13438
13683
|
() => hooks.observers?.onLLMCallComplete?.(context),
|
|
13439
|
-
|
|
13684
|
+
logger2,
|
|
13440
13685
|
"onLLMCallComplete",
|
|
13441
13686
|
true
|
|
13442
13687
|
// Cleanup after completion
|
|
@@ -13458,14 +13703,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
|
|
|
13458
13703
|
},
|
|
13459
13704
|
error: llmEvent.error,
|
|
13460
13705
|
recovered: llmEvent.recovered,
|
|
13461
|
-
logger,
|
|
13706
|
+
logger: logger2,
|
|
13462
13707
|
subagentContext
|
|
13463
13708
|
};
|
|
13464
13709
|
chainObserverCall(
|
|
13465
13710
|
llmPromiseChains,
|
|
13466
13711
|
event.nodeId,
|
|
13467
13712
|
() => hooks.observers?.onLLMCallError?.(context),
|
|
13468
|
-
|
|
13713
|
+
logger2,
|
|
13469
13714
|
"onLLMCallError",
|
|
13470
13715
|
true
|
|
13471
13716
|
// Cleanup after error
|
|
@@ -13525,6 +13770,9 @@ var init_stream_processor = __esm({
|
|
|
13525
13770
|
activeCountByGadget = /* @__PURE__ */ new Map();
|
|
13526
13771
|
/** Queue of gadgets waiting for a concurrency slot (per gadget name) */
|
|
13527
13772
|
concurrencyQueue = /* @__PURE__ */ new Map();
|
|
13773
|
+
// Exclusive gadget support
|
|
13774
|
+
/** Queue of exclusive gadgets deferred until in-flight gadgets complete */
|
|
13775
|
+
exclusiveQueue = [];
|
|
13528
13776
|
// Cross-iteration dependency tracking
|
|
13529
13777
|
/** Invocation IDs completed in previous iterations (read-only reference from Agent) */
|
|
13530
13778
|
priorCompletedInvocations;
|
|
@@ -13878,6 +14126,16 @@ var init_stream_processor = __esm({
|
|
|
13878
14126
|
if (limitResult.value === true) {
|
|
13879
14127
|
return;
|
|
13880
14128
|
}
|
|
14129
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
14130
|
+
if (gadget?.exclusive && this.inFlightExecutions.size > 0) {
|
|
14131
|
+
this.logger.debug("Deferring exclusive gadget until in-flight gadgets complete", {
|
|
14132
|
+
gadgetName: call.gadgetName,
|
|
14133
|
+
invocationId: call.invocationId,
|
|
14134
|
+
inFlightCount: this.inFlightExecutions.size
|
|
14135
|
+
});
|
|
14136
|
+
this.exclusiveQueue.push(call);
|
|
14137
|
+
return;
|
|
14138
|
+
}
|
|
13881
14139
|
const limit = this.getConcurrencyLimit(call.gadgetName);
|
|
13882
14140
|
const activeCount = this.activeCountByGadget.get(call.gadgetName) ?? 0;
|
|
13883
14141
|
if (limit > 0 && activeCount >= limit) {
|
|
@@ -14091,7 +14349,8 @@ var init_stream_processor = __esm({
|
|
|
14091
14349
|
result: result.result,
|
|
14092
14350
|
executionTimeMs: result.executionTimeMs,
|
|
14093
14351
|
cost: result.cost,
|
|
14094
|
-
media: result.media
|
|
14352
|
+
media: result.media,
|
|
14353
|
+
storedMedia: result.storedMedia
|
|
14095
14354
|
});
|
|
14096
14355
|
}
|
|
14097
14356
|
}
|
|
@@ -14163,7 +14422,7 @@ var init_stream_processor = __esm({
|
|
|
14163
14422
|
* Clears the inFlightExecutions map after all gadgets complete.
|
|
14164
14423
|
*/
|
|
14165
14424
|
async *waitForInFlightExecutions() {
|
|
14166
|
-
if (this.inFlightExecutions.size === 0 && !this.hasQueuedGadgets()) {
|
|
14425
|
+
if (this.inFlightExecutions.size === 0 && !this.hasQueuedGadgets() && this.exclusiveQueue.length === 0) {
|
|
14167
14426
|
return;
|
|
14168
14427
|
}
|
|
14169
14428
|
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
@@ -14184,6 +14443,18 @@ var init_stream_processor = __esm({
|
|
|
14184
14443
|
}
|
|
14185
14444
|
}
|
|
14186
14445
|
this.inFlightExecutions.clear();
|
|
14446
|
+
if (this.exclusiveQueue.length > 0) {
|
|
14447
|
+
this.logger.debug("Processing deferred exclusive gadgets", {
|
|
14448
|
+
count: this.exclusiveQueue.length
|
|
14449
|
+
});
|
|
14450
|
+
const queue = this.exclusiveQueue;
|
|
14451
|
+
this.exclusiveQueue = [];
|
|
14452
|
+
for (const call of queue) {
|
|
14453
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
14454
|
+
yield evt;
|
|
14455
|
+
}
|
|
14456
|
+
}
|
|
14457
|
+
}
|
|
14187
14458
|
}
|
|
14188
14459
|
/**
|
|
14189
14460
|
* Check if there are any gadgets waiting in concurrency queues.
|
|
@@ -15672,7 +15943,8 @@ var init_agent = __esm({
|
|
|
15672
15943
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
15673
15944
|
gadgetResult.invocationId,
|
|
15674
15945
|
gadgetResult.media,
|
|
15675
|
-
gadgetResult.mediaIds
|
|
15946
|
+
gadgetResult.mediaIds,
|
|
15947
|
+
gadgetResult.storedMedia
|
|
15676
15948
|
);
|
|
15677
15949
|
}
|
|
15678
15950
|
}
|