llmist 7.0.0 → 8.1.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 +410 -1038
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6073 -4
- package/dist/index.d.ts +6073 -4
- package/dist/index.js +11955 -115
- package/dist/index.js.map +1 -1
- package/package.json +8 -36
- package/LICENSE +0 -21
- package/README.md +0 -511
- package/dist/chunk-5KEZ7SQX.js +0 -1182
- package/dist/chunk-5KEZ7SQX.js.map +0 -1
- package/dist/chunk-SFZIL2VR.js +0 -12214
- package/dist/chunk-SFZIL2VR.js.map +0 -1
- package/dist/cli.cjs +0 -18226
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -7819
- package/dist/cli.js.map +0 -1
- package/dist/mock-stream-r5vjy2Iq.d.cts +0 -6397
- package/dist/mock-stream-r5vjy2Iq.d.ts +0 -6397
- package/dist/testing/index.cjs +0 -12088
- package/dist/testing/index.cjs.map +0 -1
- package/dist/testing/index.d.cts +0 -710
- package/dist/testing/index.d.ts +0 -710
- package/dist/testing/index.js +0 -83
- package/dist/testing/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,19 @@ var init_constants = __esm({
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
// src/providers/constants.ts
|
|
49
|
+
var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS, FALLBACK_CHARS_PER_TOKEN, OPENAI_MESSAGE_OVERHEAD_TOKENS, OPENAI_REPLY_PRIMING_TOKENS, OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
|
|
50
|
+
var init_constants2 = __esm({
|
|
51
|
+
"src/providers/constants.ts"() {
|
|
52
|
+
"use strict";
|
|
53
|
+
ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
|
|
54
|
+
FALLBACK_CHARS_PER_TOKEN = 4;
|
|
55
|
+
OPENAI_MESSAGE_OVERHEAD_TOKENS = 4;
|
|
56
|
+
OPENAI_REPLY_PRIMING_TOKENS = 2;
|
|
57
|
+
OPENAI_NAME_FIELD_OVERHEAD_TOKENS = 1;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
48
61
|
// src/core/input-content.ts
|
|
49
62
|
function isTextPart(part) {
|
|
50
63
|
return part.type === "text";
|
|
@@ -2577,7 +2590,138 @@ var AGENT_INTERNAL_KEY;
|
|
|
2577
2590
|
var init_agent_internal_key = __esm({
|
|
2578
2591
|
"src/agent/agent-internal-key.ts"() {
|
|
2579
2592
|
"use strict";
|
|
2580
|
-
AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
|
|
2593
|
+
AGENT_INTERNAL_KEY = /* @__PURE__ */ Symbol("AGENT_INTERNAL_KEY");
|
|
2594
|
+
}
|
|
2595
|
+
});
|
|
2596
|
+
|
|
2597
|
+
// src/core/retry.ts
|
|
2598
|
+
function resolveRetryConfig(config) {
|
|
2599
|
+
if (!config) {
|
|
2600
|
+
return { ...DEFAULT_RETRY_CONFIG };
|
|
2601
|
+
}
|
|
2602
|
+
return {
|
|
2603
|
+
enabled: config.enabled ?? DEFAULT_RETRY_CONFIG.enabled,
|
|
2604
|
+
retries: config.retries ?? DEFAULT_RETRY_CONFIG.retries,
|
|
2605
|
+
minTimeout: config.minTimeout ?? DEFAULT_RETRY_CONFIG.minTimeout,
|
|
2606
|
+
maxTimeout: config.maxTimeout ?? DEFAULT_RETRY_CONFIG.maxTimeout,
|
|
2607
|
+
factor: config.factor ?? DEFAULT_RETRY_CONFIG.factor,
|
|
2608
|
+
randomize: config.randomize ?? DEFAULT_RETRY_CONFIG.randomize,
|
|
2609
|
+
onRetry: config.onRetry,
|
|
2610
|
+
onRetriesExhausted: config.onRetriesExhausted,
|
|
2611
|
+
shouldRetry: config.shouldRetry
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
function isRetryableError(error) {
|
|
2615
|
+
const message = error.message.toLowerCase();
|
|
2616
|
+
const name = error.name;
|
|
2617
|
+
if (message.includes("429") || message.includes("rate limit") || message.includes("rate_limit")) {
|
|
2618
|
+
return true;
|
|
2619
|
+
}
|
|
2620
|
+
if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504") || message.includes("internal server error") || message.includes("bad gateway") || message.includes("service unavailable") || message.includes("gateway timeout")) {
|
|
2621
|
+
return true;
|
|
2622
|
+
}
|
|
2623
|
+
if (message.includes("timeout") || message.includes("etimedout") || message.includes("timed out")) {
|
|
2624
|
+
return true;
|
|
2625
|
+
}
|
|
2626
|
+
if (message.includes("econnreset") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("connection") || message.includes("network")) {
|
|
2627
|
+
return true;
|
|
2628
|
+
}
|
|
2629
|
+
if (name === "APIConnectionError" || name === "RateLimitError" || name === "InternalServerError" || name === "ServiceUnavailableError" || name === "APITimeoutError") {
|
|
2630
|
+
return true;
|
|
2631
|
+
}
|
|
2632
|
+
if (message.includes("overloaded") || message.includes("capacity")) {
|
|
2633
|
+
return true;
|
|
2634
|
+
}
|
|
2635
|
+
if (message.includes("401") || message.includes("403") || message.includes("400") || message.includes("404") || message.includes("authentication") || message.includes("unauthorized") || message.includes("forbidden") || message.includes("invalid") || message.includes("content policy") || name === "AuthenticationError" || name === "BadRequestError" || name === "NotFoundError" || name === "PermissionDeniedError") {
|
|
2636
|
+
return false;
|
|
2637
|
+
}
|
|
2638
|
+
return false;
|
|
2639
|
+
}
|
|
2640
|
+
function formatLLMError(error) {
|
|
2641
|
+
const message = error.message;
|
|
2642
|
+
const name = error.name;
|
|
2643
|
+
if (message.includes("RESOURCE_EXHAUSTED") || message.includes("429")) {
|
|
2644
|
+
return "Rate limit exceeded (429) - retry after a few seconds";
|
|
2645
|
+
}
|
|
2646
|
+
if (message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("rate_limit")) {
|
|
2647
|
+
return "Rate limit exceeded - retry after a few seconds";
|
|
2648
|
+
}
|
|
2649
|
+
if (message.toLowerCase().includes("overloaded") || message.toLowerCase().includes("capacity")) {
|
|
2650
|
+
return "API overloaded - retry later";
|
|
2651
|
+
}
|
|
2652
|
+
if (message.includes("500") || message.toLowerCase().includes("internal server error")) {
|
|
2653
|
+
return "Internal server error (500) - the API is experiencing issues";
|
|
2654
|
+
}
|
|
2655
|
+
if (message.includes("502") || message.toLowerCase().includes("bad gateway")) {
|
|
2656
|
+
return "Bad gateway (502) - the API is temporarily unavailable";
|
|
2657
|
+
}
|
|
2658
|
+
if (message.includes("503") || message.toLowerCase().includes("service unavailable")) {
|
|
2659
|
+
return "Service unavailable (503) - the API is temporarily down";
|
|
2660
|
+
}
|
|
2661
|
+
if (message.includes("504") || message.toLowerCase().includes("gateway timeout")) {
|
|
2662
|
+
return "Gateway timeout (504) - the request took too long";
|
|
2663
|
+
}
|
|
2664
|
+
if (message.toLowerCase().includes("timeout") || message.toLowerCase().includes("timed out")) {
|
|
2665
|
+
return "Request timed out - the API took too long to respond";
|
|
2666
|
+
}
|
|
2667
|
+
if (message.toLowerCase().includes("econnrefused")) {
|
|
2668
|
+
return "Connection refused - unable to reach the API";
|
|
2669
|
+
}
|
|
2670
|
+
if (message.toLowerCase().includes("econnreset")) {
|
|
2671
|
+
return "Connection reset - the API closed the connection";
|
|
2672
|
+
}
|
|
2673
|
+
if (message.toLowerCase().includes("enotfound")) {
|
|
2674
|
+
return "DNS error - unable to resolve API hostname";
|
|
2675
|
+
}
|
|
2676
|
+
if (message.includes("401") || message.toLowerCase().includes("unauthorized") || name === "AuthenticationError") {
|
|
2677
|
+
return "Authentication failed - check your API key";
|
|
2678
|
+
}
|
|
2679
|
+
if (message.includes("403") || message.toLowerCase().includes("forbidden") || name === "PermissionDeniedError") {
|
|
2680
|
+
return "Permission denied - your API key lacks required permissions";
|
|
2681
|
+
}
|
|
2682
|
+
if (message.includes("400") || name === "BadRequestError") {
|
|
2683
|
+
const match = message.match(/message['":\s]+['"]?([^'"}\]]+)/i);
|
|
2684
|
+
if (match) {
|
|
2685
|
+
return `Bad request: ${match[1].trim()}`;
|
|
2686
|
+
}
|
|
2687
|
+
return "Bad request - check your input parameters";
|
|
2688
|
+
}
|
|
2689
|
+
if (message.toLowerCase().includes("content policy") || message.toLowerCase().includes("safety")) {
|
|
2690
|
+
return "Content policy violation - the request was blocked";
|
|
2691
|
+
}
|
|
2692
|
+
try {
|
|
2693
|
+
const parsed = JSON.parse(message);
|
|
2694
|
+
const extractedMessage = parsed?.error?.message || parsed?.message;
|
|
2695
|
+
if (typeof extractedMessage === "string" && extractedMessage.length > 0) {
|
|
2696
|
+
return extractedMessage.trim();
|
|
2697
|
+
}
|
|
2698
|
+
} catch {
|
|
2699
|
+
}
|
|
2700
|
+
const jsonMatch = message.match(/["']?message["']?\s*[:=]\s*["']([^"']+)["']/i);
|
|
2701
|
+
if (jsonMatch) {
|
|
2702
|
+
return jsonMatch[1].trim();
|
|
2703
|
+
}
|
|
2704
|
+
if (message.length > 200) {
|
|
2705
|
+
const firstPart = message.split(/[.!?\n]/)[0];
|
|
2706
|
+
if (firstPart && firstPart.length > 10 && firstPart.length < 150) {
|
|
2707
|
+
return firstPart.trim();
|
|
2708
|
+
}
|
|
2709
|
+
return message.slice(0, 150).trim() + "...";
|
|
2710
|
+
}
|
|
2711
|
+
return message;
|
|
2712
|
+
}
|
|
2713
|
+
var DEFAULT_RETRY_CONFIG;
|
|
2714
|
+
var init_retry = __esm({
|
|
2715
|
+
"src/core/retry.ts"() {
|
|
2716
|
+
"use strict";
|
|
2717
|
+
DEFAULT_RETRY_CONFIG = {
|
|
2718
|
+
enabled: true,
|
|
2719
|
+
retries: 3,
|
|
2720
|
+
minTimeout: 1e3,
|
|
2721
|
+
maxTimeout: 3e4,
|
|
2722
|
+
factor: 2,
|
|
2723
|
+
randomize: true
|
|
2724
|
+
};
|
|
2581
2725
|
}
|
|
2582
2726
|
});
|
|
2583
2727
|
|
|
@@ -3047,6 +3191,9 @@ var init_conversation_manager = __esm({
|
|
|
3047
3191
|
}
|
|
3048
3192
|
}
|
|
3049
3193
|
}
|
|
3194
|
+
getConversationHistory() {
|
|
3195
|
+
return [...this.initialMessages, ...this.historyBuilder.build()];
|
|
3196
|
+
}
|
|
3050
3197
|
};
|
|
3051
3198
|
}
|
|
3052
3199
|
});
|
|
@@ -3689,19 +3836,6 @@ var init_base_provider = __esm({
|
|
|
3689
3836
|
}
|
|
3690
3837
|
});
|
|
3691
3838
|
|
|
3692
|
-
// src/providers/constants.ts
|
|
3693
|
-
var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS, FALLBACK_CHARS_PER_TOKEN, OPENAI_MESSAGE_OVERHEAD_TOKENS, OPENAI_REPLY_PRIMING_TOKENS, OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
|
|
3694
|
-
var init_constants2 = __esm({
|
|
3695
|
-
"src/providers/constants.ts"() {
|
|
3696
|
-
"use strict";
|
|
3697
|
-
ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
|
|
3698
|
-
FALLBACK_CHARS_PER_TOKEN = 4;
|
|
3699
|
-
OPENAI_MESSAGE_OVERHEAD_TOKENS = 4;
|
|
3700
|
-
OPENAI_REPLY_PRIMING_TOKENS = 2;
|
|
3701
|
-
OPENAI_NAME_FIELD_OVERHEAD_TOKENS = 1;
|
|
3702
|
-
}
|
|
3703
|
-
});
|
|
3704
|
-
|
|
3705
3839
|
// src/providers/utils.ts
|
|
3706
3840
|
function readEnvVar(key) {
|
|
3707
3841
|
if (typeof process === "undefined" || typeof process.env === "undefined") {
|
|
@@ -7807,14 +7941,16 @@ var init_executor = __esm({
|
|
|
7807
7941
|
executionTimeMs: Date.now() - startTime
|
|
7808
7942
|
};
|
|
7809
7943
|
}
|
|
7810
|
-
|
|
7944
|
+
const isHumanInputError = error instanceof Error && error.name === "HumanInputRequiredException" && "question" in error;
|
|
7945
|
+
if (isHumanInputError) {
|
|
7946
|
+
const question = error.question;
|
|
7811
7947
|
this.logger.info("Gadget requested human input", {
|
|
7812
7948
|
gadgetName: call.gadgetName,
|
|
7813
|
-
question
|
|
7949
|
+
question
|
|
7814
7950
|
});
|
|
7815
7951
|
if (this.requestHumanInput) {
|
|
7816
7952
|
try {
|
|
7817
|
-
const answer = await this.requestHumanInput(
|
|
7953
|
+
const answer = await this.requestHumanInput(question);
|
|
7818
7954
|
this.logger.debug("Human input received", {
|
|
7819
7955
|
gadgetName: call.gadgetName,
|
|
7820
7956
|
answerLength: answer.length
|
|
@@ -8624,7 +8760,7 @@ var init_stream_processor = __esm({
|
|
|
8624
8760
|
});
|
|
8625
8761
|
|
|
8626
8762
|
// src/agent/agent.ts
|
|
8627
|
-
var Agent;
|
|
8763
|
+
var import_p_retry, Agent;
|
|
8628
8764
|
var init_agent = __esm({
|
|
8629
8765
|
"src/agent/agent.ts"() {
|
|
8630
8766
|
"use strict";
|
|
@@ -8636,6 +8772,8 @@ var init_agent = __esm({
|
|
|
8636
8772
|
init_output_viewer();
|
|
8637
8773
|
init_logger();
|
|
8638
8774
|
init_agent_internal_key();
|
|
8775
|
+
init_retry();
|
|
8776
|
+
import_p_retry = __toESM(require("p-retry"), 1);
|
|
8639
8777
|
init_manager();
|
|
8640
8778
|
init_conversation_manager();
|
|
8641
8779
|
init_event_handlers();
|
|
@@ -8670,6 +8808,8 @@ var init_agent = __esm({
|
|
|
8670
8808
|
mediaStore;
|
|
8671
8809
|
// Cancellation
|
|
8672
8810
|
signal;
|
|
8811
|
+
// Retry configuration
|
|
8812
|
+
retryConfig;
|
|
8673
8813
|
// Subagent configuration
|
|
8674
8814
|
agentContextConfig;
|
|
8675
8815
|
subagentConfig;
|
|
@@ -8684,6 +8824,8 @@ var init_agent = __esm({
|
|
|
8684
8824
|
// Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
|
|
8685
8825
|
completedInvocationIds = /* @__PURE__ */ new Set();
|
|
8686
8826
|
failedInvocationIds = /* @__PURE__ */ new Set();
|
|
8827
|
+
// Queue for user messages injected during agent execution (REPL mid-session input)
|
|
8828
|
+
pendingUserMessages = [];
|
|
8687
8829
|
// Execution Tree - first-class model for nested subagent support
|
|
8688
8830
|
tree;
|
|
8689
8831
|
parentNodeId;
|
|
@@ -8758,6 +8900,7 @@ var init_agent = __esm({
|
|
|
8758
8900
|
);
|
|
8759
8901
|
}
|
|
8760
8902
|
this.signal = options.signal;
|
|
8903
|
+
this.retryConfig = resolveRetryConfig(options.retryConfig);
|
|
8761
8904
|
this.agentContextConfig = {
|
|
8762
8905
|
model: this.model,
|
|
8763
8906
|
temperature: this.temperature
|
|
@@ -8975,6 +9118,44 @@ var init_agent = __esm({
|
|
|
8975
9118
|
getCompactionStats() {
|
|
8976
9119
|
return this.compactionManager?.getStats() ?? null;
|
|
8977
9120
|
}
|
|
9121
|
+
/**
|
|
9122
|
+
* Get the conversation manager for this agent.
|
|
9123
|
+
* Used by REPL mode to extract session history for continuation.
|
|
9124
|
+
*
|
|
9125
|
+
* @returns The conversation manager containing all messages
|
|
9126
|
+
*
|
|
9127
|
+
* @example
|
|
9128
|
+
* ```typescript
|
|
9129
|
+
* // After running agent, extract history for next session
|
|
9130
|
+
* const history = agent.getConversation().getConversationHistory();
|
|
9131
|
+
* // Pass to next agent via builder.withHistory()
|
|
9132
|
+
* ```
|
|
9133
|
+
*/
|
|
9134
|
+
getConversation() {
|
|
9135
|
+
return this.conversation;
|
|
9136
|
+
}
|
|
9137
|
+
/**
|
|
9138
|
+
* Inject a user message to be processed in the next iteration.
|
|
9139
|
+
* Used by REPL mode to allow user input during a running session.
|
|
9140
|
+
*
|
|
9141
|
+
* The message is queued and will be added to the conversation before
|
|
9142
|
+
* the next LLM call. This allows users to provide additional context
|
|
9143
|
+
* or instructions while the agent is executing.
|
|
9144
|
+
*
|
|
9145
|
+
* @param message - The user message to inject
|
|
9146
|
+
*
|
|
9147
|
+
* @example
|
|
9148
|
+
* ```typescript
|
|
9149
|
+
* // While agent is running in TUI:
|
|
9150
|
+
* tui.onMidSessionInput((msg) => {
|
|
9151
|
+
* agent.injectUserMessage(msg);
|
|
9152
|
+
* });
|
|
9153
|
+
* ```
|
|
9154
|
+
*/
|
|
9155
|
+
injectUserMessage(message) {
|
|
9156
|
+
this.pendingUserMessages.push(message);
|
|
9157
|
+
this.logger.debug("User message queued for injection", { message });
|
|
9158
|
+
}
|
|
8978
9159
|
/**
|
|
8979
9160
|
* Run the agent loop.
|
|
8980
9161
|
* Clean, simple orchestration - all complexity is in StreamProcessor.
|
|
@@ -8996,6 +9177,14 @@ var init_agent = __esm({
|
|
|
8996
9177
|
if (await this.checkAbortAndNotify(currentIteration)) {
|
|
8997
9178
|
return;
|
|
8998
9179
|
}
|
|
9180
|
+
while (this.pendingUserMessages.length > 0) {
|
|
9181
|
+
const msg = this.pendingUserMessages.shift();
|
|
9182
|
+
this.conversation.addUserMessage(msg);
|
|
9183
|
+
this.logger.info("Injected user message into conversation", {
|
|
9184
|
+
iteration: currentIteration,
|
|
9185
|
+
messageLength: msg.length
|
|
9186
|
+
});
|
|
9187
|
+
}
|
|
8999
9188
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
9000
9189
|
try {
|
|
9001
9190
|
const compactionEvent = await this.checkAndPerformCompaction(currentIteration);
|
|
@@ -9024,7 +9213,7 @@ var init_agent = __esm({
|
|
|
9024
9213
|
request: llmOptions.messages
|
|
9025
9214
|
});
|
|
9026
9215
|
const currentLLMNodeId = llmNode.id;
|
|
9027
|
-
const stream2 = this.
|
|
9216
|
+
const stream2 = await this.createStreamWithRetry(llmOptions, currentIteration);
|
|
9028
9217
|
const processor = new StreamProcessor({
|
|
9029
9218
|
iteration: currentIteration,
|
|
9030
9219
|
registry: this.registry,
|
|
@@ -9149,6 +9338,53 @@ var init_agent = __esm({
|
|
|
9149
9338
|
reason: currentIteration >= this.maxIterations ? "max_iterations" : "natural_completion"
|
|
9150
9339
|
});
|
|
9151
9340
|
}
|
|
9341
|
+
/**
|
|
9342
|
+
* Create LLM stream with retry logic.
|
|
9343
|
+
* Wraps the stream creation with exponential backoff for transient failures.
|
|
9344
|
+
*/
|
|
9345
|
+
async createStreamWithRetry(llmOptions, iteration) {
|
|
9346
|
+
if (!this.retryConfig.enabled) {
|
|
9347
|
+
return this.client.stream(llmOptions);
|
|
9348
|
+
}
|
|
9349
|
+
const { retries, minTimeout, maxTimeout, factor, randomize, onRetry, onRetriesExhausted, shouldRetry } = this.retryConfig;
|
|
9350
|
+
try {
|
|
9351
|
+
return await (0, import_p_retry.default)(
|
|
9352
|
+
async (attemptNumber) => {
|
|
9353
|
+
this.logger.debug("Creating LLM stream", { attempt: attemptNumber, maxAttempts: retries + 1 });
|
|
9354
|
+
return this.client.stream(llmOptions);
|
|
9355
|
+
},
|
|
9356
|
+
{
|
|
9357
|
+
retries,
|
|
9358
|
+
minTimeout,
|
|
9359
|
+
maxTimeout,
|
|
9360
|
+
factor,
|
|
9361
|
+
randomize,
|
|
9362
|
+
signal: this.signal,
|
|
9363
|
+
onFailedAttempt: (context) => {
|
|
9364
|
+
const { error, attemptNumber, retriesLeft } = context;
|
|
9365
|
+
this.logger.warn(
|
|
9366
|
+
`LLM call failed (attempt ${attemptNumber}/${attemptNumber + retriesLeft}), retrying...`,
|
|
9367
|
+
{ error: error.message, retriesLeft }
|
|
9368
|
+
);
|
|
9369
|
+
onRetry?.(error, attemptNumber);
|
|
9370
|
+
},
|
|
9371
|
+
shouldRetry: (context) => {
|
|
9372
|
+
if (shouldRetry) {
|
|
9373
|
+
return shouldRetry(context.error);
|
|
9374
|
+
}
|
|
9375
|
+
return isRetryableError(context.error);
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
);
|
|
9379
|
+
} catch (error) {
|
|
9380
|
+
this.logger.error(`LLM call failed after ${retries + 1} attempts`, {
|
|
9381
|
+
error: error.message,
|
|
9382
|
+
iteration
|
|
9383
|
+
});
|
|
9384
|
+
onRetriesExhausted?.(error, retries + 1);
|
|
9385
|
+
throw error;
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9152
9388
|
/**
|
|
9153
9389
|
* Handle LLM error through controller.
|
|
9154
9390
|
*/
|
|
@@ -9526,6 +9762,7 @@ var init_builder = __esm({
|
|
|
9526
9762
|
gadgetOutputLimit;
|
|
9527
9763
|
gadgetOutputLimitPercent;
|
|
9528
9764
|
compactionConfig;
|
|
9765
|
+
retryConfig;
|
|
9529
9766
|
signal;
|
|
9530
9767
|
trailingMessage;
|
|
9531
9768
|
subagentConfig;
|
|
@@ -9695,6 +9932,59 @@ var init_builder = __esm({
|
|
|
9695
9932
|
addMessage(message) {
|
|
9696
9933
|
return this.withHistory([message]);
|
|
9697
9934
|
}
|
|
9935
|
+
/**
|
|
9936
|
+
* Clear any previously set conversation history.
|
|
9937
|
+
* Used before setting new cumulative history in REPL mode.
|
|
9938
|
+
*
|
|
9939
|
+
* @returns This builder for chaining
|
|
9940
|
+
*
|
|
9941
|
+
* @example
|
|
9942
|
+
* ```typescript
|
|
9943
|
+
* // Reset history before setting new cumulative history
|
|
9944
|
+
* builder.clearHistory().withHistory(cumulativeHistory);
|
|
9945
|
+
* ```
|
|
9946
|
+
*/
|
|
9947
|
+
clearHistory() {
|
|
9948
|
+
this.initialMessages = [];
|
|
9949
|
+
return this;
|
|
9950
|
+
}
|
|
9951
|
+
/**
|
|
9952
|
+
* Continue conversation from a previous agent's history.
|
|
9953
|
+
* Extracts full conversation history and sets it as initial messages.
|
|
9954
|
+
*
|
|
9955
|
+
* This is the recommended way to implement REPL session continuation.
|
|
9956
|
+
* It automatically handles history extraction and format conversion.
|
|
9957
|
+
*
|
|
9958
|
+
* @param agent - The previous agent to continue from
|
|
9959
|
+
* @returns This builder for chaining
|
|
9960
|
+
*
|
|
9961
|
+
* @example
|
|
9962
|
+
* ```typescript
|
|
9963
|
+
* // REPL loop with session continuity
|
|
9964
|
+
* let previousAgent: Agent | null = null;
|
|
9965
|
+
*
|
|
9966
|
+
* while (true) {
|
|
9967
|
+
* if (previousAgent) {
|
|
9968
|
+
* builder.continueFrom(previousAgent);
|
|
9969
|
+
* }
|
|
9970
|
+
* const agent = builder.ask(prompt);
|
|
9971
|
+
* await runAgent(agent);
|
|
9972
|
+
* previousAgent = agent;
|
|
9973
|
+
* }
|
|
9974
|
+
* ```
|
|
9975
|
+
*/
|
|
9976
|
+
continueFrom(agent) {
|
|
9977
|
+
const history = agent.getConversation().getConversationHistory();
|
|
9978
|
+
this.clearHistory();
|
|
9979
|
+
for (const msg of history) {
|
|
9980
|
+
if (msg.role === "user") {
|
|
9981
|
+
this.initialMessages.push({ role: "user", content: msg.content });
|
|
9982
|
+
} else if (msg.role === "assistant") {
|
|
9983
|
+
this.initialMessages.push({ role: "assistant", content: msg.content });
|
|
9984
|
+
}
|
|
9985
|
+
}
|
|
9986
|
+
return this;
|
|
9987
|
+
}
|
|
9698
9988
|
/**
|
|
9699
9989
|
* Set the human input handler for interactive conversations.
|
|
9700
9990
|
*
|
|
@@ -9923,6 +10213,60 @@ var init_builder = __esm({
|
|
|
9923
10213
|
this.compactionConfig = { enabled: false };
|
|
9924
10214
|
return this;
|
|
9925
10215
|
}
|
|
10216
|
+
/**
|
|
10217
|
+
* Configure retry behavior for LLM API calls.
|
|
10218
|
+
*
|
|
10219
|
+
* Retry is enabled by default with conservative settings (3 retries, exponential backoff).
|
|
10220
|
+
* Use this method to customize retry behavior for rate limits, timeouts, and transient errors.
|
|
10221
|
+
*
|
|
10222
|
+
* @param config - Retry configuration options
|
|
10223
|
+
* @returns This builder for chaining
|
|
10224
|
+
*
|
|
10225
|
+
* @example
|
|
10226
|
+
* ```typescript
|
|
10227
|
+
* // Custom retry configuration
|
|
10228
|
+
* .withRetry({
|
|
10229
|
+
* retries: 5,
|
|
10230
|
+
* minTimeout: 2000,
|
|
10231
|
+
* maxTimeout: 60000,
|
|
10232
|
+
* })
|
|
10233
|
+
*
|
|
10234
|
+
* // With monitoring callbacks
|
|
10235
|
+
* .withRetry({
|
|
10236
|
+
* onRetry: (error, attempt) => {
|
|
10237
|
+
* console.log(`Retry ${attempt}: ${error.message}`);
|
|
10238
|
+
* },
|
|
10239
|
+
* onRetriesExhausted: (error, attempts) => {
|
|
10240
|
+
* alerting.warn(`Failed after ${attempts} attempts`);
|
|
10241
|
+
* }
|
|
10242
|
+
* })
|
|
10243
|
+
*
|
|
10244
|
+
* // Custom retry logic
|
|
10245
|
+
* .withRetry({
|
|
10246
|
+
* shouldRetry: (error) => error.message.includes('429'),
|
|
10247
|
+
* })
|
|
10248
|
+
* ```
|
|
10249
|
+
*/
|
|
10250
|
+
withRetry(config) {
|
|
10251
|
+
this.retryConfig = { ...config, enabled: config.enabled ?? true };
|
|
10252
|
+
return this;
|
|
10253
|
+
}
|
|
10254
|
+
/**
|
|
10255
|
+
* Disable automatic retry for LLM API calls.
|
|
10256
|
+
*
|
|
10257
|
+
* By default, retry is enabled. Use this method to explicitly disable it.
|
|
10258
|
+
*
|
|
10259
|
+
* @returns This builder for chaining
|
|
10260
|
+
*
|
|
10261
|
+
* @example
|
|
10262
|
+
* ```typescript
|
|
10263
|
+
* .withoutRetry() // Disable automatic retry
|
|
10264
|
+
* ```
|
|
10265
|
+
*/
|
|
10266
|
+
withoutRetry() {
|
|
10267
|
+
this.retryConfig = { enabled: false };
|
|
10268
|
+
return this;
|
|
10269
|
+
}
|
|
9926
10270
|
/**
|
|
9927
10271
|
* Set an abort signal for cancelling requests mid-flight.
|
|
9928
10272
|
*
|
|
@@ -10240,6 +10584,7 @@ ${endPrefix}`
|
|
|
10240
10584
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
10241
10585
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
10242
10586
|
compactionConfig: this.compactionConfig,
|
|
10587
|
+
retryConfig: this.retryConfig,
|
|
10243
10588
|
signal: this.signal,
|
|
10244
10589
|
subagentConfig: this.subagentConfig,
|
|
10245
10590
|
onSubagentEvent: this.subagentEventCallback,
|
|
@@ -10425,6 +10770,7 @@ ${endPrefix}`
|
|
|
10425
10770
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
10426
10771
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
10427
10772
|
compactionConfig: this.compactionConfig,
|
|
10773
|
+
retryConfig: this.retryConfig,
|
|
10428
10774
|
signal: this.signal,
|
|
10429
10775
|
subagentConfig: this.subagentConfig,
|
|
10430
10776
|
onSubagentEvent: this.subagentEventCallback,
|
|
@@ -10444,6 +10790,7 @@ var index_exports = {};
|
|
|
10444
10790
|
__export(index_exports, {
|
|
10445
10791
|
AbortException: () => AbortException,
|
|
10446
10792
|
AbstractGadget: () => AbstractGadget,
|
|
10793
|
+
Agent: () => Agent,
|
|
10447
10794
|
AgentBuilder: () => AgentBuilder,
|
|
10448
10795
|
AnthropicMessagesProvider: () => AnthropicMessagesProvider,
|
|
10449
10796
|
CompactionManager: () => CompactionManager,
|
|
@@ -10451,8 +10798,13 @@ __export(index_exports, {
|
|
|
10451
10798
|
DEFAULT_COMPACTION_CONFIG: () => DEFAULT_COMPACTION_CONFIG,
|
|
10452
10799
|
DEFAULT_HINTS: () => DEFAULT_HINTS,
|
|
10453
10800
|
DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
|
|
10801
|
+
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
10454
10802
|
DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
|
|
10455
10803
|
ExecutionTree: () => ExecutionTree,
|
|
10804
|
+
FALLBACK_CHARS_PER_TOKEN: () => FALLBACK_CHARS_PER_TOKEN,
|
|
10805
|
+
GADGET_ARG_PREFIX: () => GADGET_ARG_PREFIX,
|
|
10806
|
+
GADGET_END_PREFIX: () => GADGET_END_PREFIX,
|
|
10807
|
+
GADGET_START_PREFIX: () => GADGET_START_PREFIX,
|
|
10456
10808
|
Gadget: () => Gadget,
|
|
10457
10809
|
GadgetCallParser: () => GadgetCallParser,
|
|
10458
10810
|
GadgetExecutor: () => GadgetExecutor,
|
|
@@ -10466,9 +10818,6 @@ __export(index_exports, {
|
|
|
10466
10818
|
LLMist: () => LLMist,
|
|
10467
10819
|
MODEL_ALIASES: () => MODEL_ALIASES,
|
|
10468
10820
|
MediaStore: () => MediaStore,
|
|
10469
|
-
MockBuilder: () => MockBuilder,
|
|
10470
|
-
MockManager: () => MockManager,
|
|
10471
|
-
MockProviderAdapter: () => MockProviderAdapter,
|
|
10472
10821
|
ModelIdentifierParser: () => ModelIdentifierParser,
|
|
10473
10822
|
ModelRegistry: () => ModelRegistry,
|
|
10474
10823
|
OpenAIChatProvider: () => OpenAIChatProvider,
|
|
@@ -10489,11 +10838,7 @@ __export(index_exports, {
|
|
|
10489
10838
|
createHints: () => createHints,
|
|
10490
10839
|
createLogger: () => createLogger,
|
|
10491
10840
|
createMediaOutput: () => createMediaOutput,
|
|
10492
|
-
createMockAdapter: () => createMockAdapter,
|
|
10493
|
-
createMockClient: () => createMockClient,
|
|
10494
|
-
createMockStream: () => createMockStream,
|
|
10495
10841
|
createOpenAIProviderFromEnv: () => createOpenAIProviderFromEnv,
|
|
10496
|
-
createTextMockStream: () => createTextMockStream,
|
|
10497
10842
|
defaultLogger: () => defaultLogger,
|
|
10498
10843
|
detectAudioMimeType: () => detectAudioMimeType,
|
|
10499
10844
|
detectImageMimeType: () => detectImageMimeType,
|
|
@@ -10502,8 +10847,8 @@ __export(index_exports, {
|
|
|
10502
10847
|
filterByDepth: () => filterByDepth,
|
|
10503
10848
|
filterByParent: () => filterByParent,
|
|
10504
10849
|
filterRootEvents: () => filterRootEvents,
|
|
10850
|
+
formatLLMError: () => formatLLMError,
|
|
10505
10851
|
getHostExports: () => getHostExports,
|
|
10506
|
-
getMockManager: () => getMockManager,
|
|
10507
10852
|
getModelId: () => getModelId,
|
|
10508
10853
|
getProvider: () => getProvider,
|
|
10509
10854
|
groupByParent: () => groupByParent,
|
|
@@ -10511,16 +10856,17 @@ __export(index_exports, {
|
|
|
10511
10856
|
imageFromBase64: () => imageFromBase64,
|
|
10512
10857
|
imageFromBuffer: () => imageFromBuffer,
|
|
10513
10858
|
imageFromUrl: () => imageFromUrl,
|
|
10859
|
+
isAbortError: () => isAbortError,
|
|
10514
10860
|
isAudioPart: () => isAudioPart,
|
|
10515
10861
|
isDataUrl: () => isDataUrl,
|
|
10516
10862
|
isGadgetEvent: () => isGadgetEvent,
|
|
10517
10863
|
isImagePart: () => isImagePart,
|
|
10518
10864
|
isLLMEvent: () => isLLMEvent,
|
|
10865
|
+
isRetryableError: () => isRetryableError,
|
|
10519
10866
|
isRootEvent: () => isRootEvent,
|
|
10520
10867
|
isSubagentEvent: () => isSubagentEvent,
|
|
10521
10868
|
isTextPart: () => isTextPart,
|
|
10522
10869
|
iterationProgressHint: () => iterationProgressHint,
|
|
10523
|
-
mockLLM: () => mockLLM,
|
|
10524
10870
|
normalizeMessageContent: () => normalizeMessageContent,
|
|
10525
10871
|
parallelGadgetHint: () => parallelGadgetHint,
|
|
10526
10872
|
parseDataUrl: () => parseDataUrl,
|
|
@@ -10528,6 +10874,7 @@ __export(index_exports, {
|
|
|
10528
10874
|
resolveHintTemplate: () => resolveHintTemplate,
|
|
10529
10875
|
resolveModel: () => resolveModel,
|
|
10530
10876
|
resolvePromptTemplate: () => resolvePromptTemplate,
|
|
10877
|
+
resolveRetryConfig: () => resolveRetryConfig,
|
|
10531
10878
|
resolveRulesTemplate: () => resolveRulesTemplate,
|
|
10532
10879
|
resolveSubagentModel: () => resolveSubagentModel,
|
|
10533
10880
|
resolveValue: () => resolveValue,
|
|
@@ -10537,15 +10884,19 @@ __export(index_exports, {
|
|
|
10537
10884
|
resultWithImages: () => resultWithImages,
|
|
10538
10885
|
resultWithMedia: () => resultWithMedia,
|
|
10539
10886
|
runWithHandlers: () => runWithHandlers,
|
|
10887
|
+
schemaToJSONSchema: () => schemaToJSONSchema,
|
|
10540
10888
|
stream: () => stream,
|
|
10541
10889
|
text: () => text,
|
|
10542
10890
|
toBase64: () => toBase64,
|
|
10543
10891
|
validateAndApplyDefaults: () => validateAndApplyDefaults,
|
|
10544
10892
|
validateGadgetParams: () => validateGadgetParams,
|
|
10893
|
+
validateGadgetSchema: () => validateGadgetSchema,
|
|
10545
10894
|
z: () => import_zod3.z
|
|
10546
10895
|
});
|
|
10547
10896
|
module.exports = __toCommonJS(index_exports);
|
|
10548
10897
|
var import_zod3 = require("zod");
|
|
10898
|
+
init_constants();
|
|
10899
|
+
init_constants2();
|
|
10549
10900
|
init_builder();
|
|
10550
10901
|
init_event_handlers();
|
|
10551
10902
|
|
|
@@ -11328,7 +11679,6 @@ var HookPresets = class _HookPresets {
|
|
|
11328
11679
|
init_config();
|
|
11329
11680
|
init_manager();
|
|
11330
11681
|
init_strategies();
|
|
11331
|
-
init_strategy();
|
|
11332
11682
|
|
|
11333
11683
|
// src/agent/index.ts
|
|
11334
11684
|
init_conversation_manager();
|
|
@@ -11436,6 +11786,23 @@ init_stream_processor();
|
|
|
11436
11786
|
|
|
11437
11787
|
// src/index.ts
|
|
11438
11788
|
init_client();
|
|
11789
|
+
|
|
11790
|
+
// src/core/errors.ts
|
|
11791
|
+
function isAbortError(error) {
|
|
11792
|
+
if (!(error instanceof Error)) return false;
|
|
11793
|
+
if (error.name === "AbortError") return true;
|
|
11794
|
+
if (error.name === "APIConnectionAbortedError") return true;
|
|
11795
|
+
if (error.name === "APIUserAbortError") return true;
|
|
11796
|
+
const message = error.message.toLowerCase();
|
|
11797
|
+
if (message.includes("abort")) return true;
|
|
11798
|
+
if (message.includes("cancelled")) return true;
|
|
11799
|
+
if (message.includes("canceled")) return true;
|
|
11800
|
+
return false;
|
|
11801
|
+
}
|
|
11802
|
+
|
|
11803
|
+
// src/index.ts
|
|
11804
|
+
init_agent();
|
|
11805
|
+
init_retry();
|
|
11439
11806
|
init_execution_tree();
|
|
11440
11807
|
|
|
11441
11808
|
// src/core/execution-events.ts
|
|
@@ -11633,6 +12000,8 @@ function validateGadgetParams(gadget, params) {
|
|
|
11633
12000
|
}
|
|
11634
12001
|
|
|
11635
12002
|
// src/index.ts
|
|
12003
|
+
init_schema_to_json();
|
|
12004
|
+
init_schema_validator();
|
|
11636
12005
|
init_logger();
|
|
11637
12006
|
|
|
11638
12007
|
// src/utils/config-resolver.ts
|
|
@@ -11684,1006 +12053,6 @@ init_anthropic();
|
|
|
11684
12053
|
init_discovery();
|
|
11685
12054
|
init_gemini();
|
|
11686
12055
|
init_openai();
|
|
11687
|
-
|
|
11688
|
-
// src/testing/cli-helpers.ts
|
|
11689
|
-
var import_node_stream = require("stream");
|
|
11690
|
-
|
|
11691
|
-
// src/testing/mock-manager.ts
|
|
11692
|
-
init_logger();
|
|
11693
|
-
var MockManager = class _MockManager {
|
|
11694
|
-
static instance = null;
|
|
11695
|
-
mocks = /* @__PURE__ */ new Map();
|
|
11696
|
-
stats = /* @__PURE__ */ new Map();
|
|
11697
|
-
options;
|
|
11698
|
-
logger;
|
|
11699
|
-
nextId = 1;
|
|
11700
|
-
constructor(options = {}) {
|
|
11701
|
-
this.options = {
|
|
11702
|
-
strictMode: options.strictMode ?? false,
|
|
11703
|
-
debug: options.debug ?? false,
|
|
11704
|
-
recordStats: options.recordStats ?? true
|
|
11705
|
-
};
|
|
11706
|
-
this.logger = createLogger({ name: "MockManager", minLevel: this.options.debug ? 2 : 3 });
|
|
11707
|
-
}
|
|
11708
|
-
/**
|
|
11709
|
-
* Get the global MockManager instance.
|
|
11710
|
-
* Creates one if it doesn't exist.
|
|
11711
|
-
*/
|
|
11712
|
-
static getInstance(options) {
|
|
11713
|
-
if (!_MockManager.instance) {
|
|
11714
|
-
_MockManager.instance = new _MockManager(options);
|
|
11715
|
-
} else if (options) {
|
|
11716
|
-
console.warn(
|
|
11717
|
-
"MockManager.getInstance() called with options, but instance already exists. Options are ignored. Use setOptions() to update options or reset() to reinitialize."
|
|
11718
|
-
);
|
|
11719
|
-
}
|
|
11720
|
-
return _MockManager.instance;
|
|
11721
|
-
}
|
|
11722
|
-
/**
|
|
11723
|
-
* Reset the global instance (useful for testing).
|
|
11724
|
-
*/
|
|
11725
|
-
static reset() {
|
|
11726
|
-
_MockManager.instance = null;
|
|
11727
|
-
}
|
|
11728
|
-
/**
|
|
11729
|
-
* Register a new mock.
|
|
11730
|
-
*
|
|
11731
|
-
* @param registration - The mock registration configuration
|
|
11732
|
-
* @returns The ID of the registered mock
|
|
11733
|
-
*
|
|
11734
|
-
* @example
|
|
11735
|
-
* const manager = MockManager.getInstance();
|
|
11736
|
-
* const mockId = manager.register({
|
|
11737
|
-
* label: 'GPT-4 mock',
|
|
11738
|
-
* matcher: (ctx) => ctx.modelName.includes('gpt-4'),
|
|
11739
|
-
* response: { text: 'Mocked response' }
|
|
11740
|
-
* });
|
|
11741
|
-
*/
|
|
11742
|
-
register(registration) {
|
|
11743
|
-
const id = registration.id ?? `mock-${this.nextId++}`;
|
|
11744
|
-
const mock = {
|
|
11745
|
-
id,
|
|
11746
|
-
matcher: registration.matcher,
|
|
11747
|
-
response: registration.response,
|
|
11748
|
-
label: registration.label,
|
|
11749
|
-
once: registration.once
|
|
11750
|
-
};
|
|
11751
|
-
this.mocks.set(id, mock);
|
|
11752
|
-
if (this.options.recordStats) {
|
|
11753
|
-
this.stats.set(id, { matchCount: 0 });
|
|
11754
|
-
}
|
|
11755
|
-
this.logger.debug(
|
|
11756
|
-
`Registered mock: ${id}${mock.label ? ` (${mock.label})` : ""}${mock.once ? " [once]" : ""}`
|
|
11757
|
-
);
|
|
11758
|
-
return id;
|
|
11759
|
-
}
|
|
11760
|
-
/**
|
|
11761
|
-
* Unregister a mock by ID.
|
|
11762
|
-
*/
|
|
11763
|
-
unregister(id) {
|
|
11764
|
-
const deleted = this.mocks.delete(id);
|
|
11765
|
-
if (deleted) {
|
|
11766
|
-
this.stats.delete(id);
|
|
11767
|
-
this.logger.debug(`Unregistered mock: ${id}`);
|
|
11768
|
-
}
|
|
11769
|
-
return deleted;
|
|
11770
|
-
}
|
|
11771
|
-
/**
|
|
11772
|
-
* Clear all registered mocks.
|
|
11773
|
-
*/
|
|
11774
|
-
clear() {
|
|
11775
|
-
this.mocks.clear();
|
|
11776
|
-
this.stats.clear();
|
|
11777
|
-
this.logger.debug("Cleared all mocks");
|
|
11778
|
-
}
|
|
11779
|
-
/**
|
|
11780
|
-
* Find and return a matching mock for the given context.
|
|
11781
|
-
* Returns the mock response if found, null otherwise.
|
|
11782
|
-
*/
|
|
11783
|
-
async findMatch(context) {
|
|
11784
|
-
this.logger.debug(
|
|
11785
|
-
`Finding match for: ${context.provider}:${context.modelName} (${this.mocks.size} mocks registered)`
|
|
11786
|
-
);
|
|
11787
|
-
for (const [id, mock] of this.mocks.entries()) {
|
|
11788
|
-
let matches = false;
|
|
11789
|
-
try {
|
|
11790
|
-
matches = await Promise.resolve(mock.matcher(context));
|
|
11791
|
-
} catch (error) {
|
|
11792
|
-
this.logger.warn(`Error in matcher ${id}:`, error);
|
|
11793
|
-
if (this.options.strictMode) {
|
|
11794
|
-
throw new Error(`Matcher error in mock ${id}: ${error}`);
|
|
11795
|
-
}
|
|
11796
|
-
continue;
|
|
11797
|
-
}
|
|
11798
|
-
if (matches) {
|
|
11799
|
-
this.logger.debug(`Mock matched: ${id}${mock.label ? ` (${mock.label})` : ""}`);
|
|
11800
|
-
if (this.options.recordStats) {
|
|
11801
|
-
const stats = this.stats.get(id);
|
|
11802
|
-
if (stats) {
|
|
11803
|
-
stats.matchCount++;
|
|
11804
|
-
stats.lastUsed = /* @__PURE__ */ new Date();
|
|
11805
|
-
}
|
|
11806
|
-
}
|
|
11807
|
-
if (mock.once) {
|
|
11808
|
-
this.mocks.delete(id);
|
|
11809
|
-
this.stats.delete(id);
|
|
11810
|
-
this.logger.debug(`Removed one-time mock: ${id}`);
|
|
11811
|
-
}
|
|
11812
|
-
const response = typeof mock.response === "function" ? await Promise.resolve(mock.response(context)) : mock.response;
|
|
11813
|
-
return response;
|
|
11814
|
-
}
|
|
11815
|
-
}
|
|
11816
|
-
this.logger.debug("No mock matched");
|
|
11817
|
-
if (this.options.strictMode) {
|
|
11818
|
-
throw new Error(
|
|
11819
|
-
`No mock registered for ${context.provider}:${context.modelName}. Register a mock using MockManager.getInstance().register() or disable strictMode.`
|
|
11820
|
-
);
|
|
11821
|
-
}
|
|
11822
|
-
return {
|
|
11823
|
-
text: "",
|
|
11824
|
-
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
11825
|
-
finishReason: "stop"
|
|
11826
|
-
};
|
|
11827
|
-
}
|
|
11828
|
-
/**
|
|
11829
|
-
* Get statistics for a specific mock.
|
|
11830
|
-
*/
|
|
11831
|
-
getStats(id) {
|
|
11832
|
-
return this.stats.get(id);
|
|
11833
|
-
}
|
|
11834
|
-
/**
|
|
11835
|
-
* Get all registered mock IDs.
|
|
11836
|
-
*/
|
|
11837
|
-
getMockIds() {
|
|
11838
|
-
return Array.from(this.mocks.keys());
|
|
11839
|
-
}
|
|
11840
|
-
/**
|
|
11841
|
-
* Get the number of registered mocks.
|
|
11842
|
-
*/
|
|
11843
|
-
getCount() {
|
|
11844
|
-
return this.mocks.size;
|
|
11845
|
-
}
|
|
11846
|
-
/**
|
|
11847
|
-
* Update the mock manager options.
|
|
11848
|
-
*/
|
|
11849
|
-
setOptions(options) {
|
|
11850
|
-
this.options = { ...this.options, ...options };
|
|
11851
|
-
this.logger = createLogger({ name: "MockManager", minLevel: this.options.debug ? 2 : 3 });
|
|
11852
|
-
}
|
|
11853
|
-
};
|
|
11854
|
-
function getMockManager(options) {
|
|
11855
|
-
return MockManager.getInstance(options);
|
|
11856
|
-
}
|
|
11857
|
-
|
|
11858
|
-
// src/testing/mock-stream.ts
|
|
11859
|
-
init_constants();
|
|
11860
|
-
function sleep(ms) {
|
|
11861
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11862
|
-
}
|
|
11863
|
-
function generateInvocationId() {
|
|
11864
|
-
return `inv-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
11865
|
-
}
|
|
11866
|
-
function splitIntoChunks(text3, minChunkSize = 5, maxChunkSize = 30) {
|
|
11867
|
-
const chunks = [];
|
|
11868
|
-
let remaining = text3;
|
|
11869
|
-
while (remaining.length > 0) {
|
|
11870
|
-
const chunkSize = Math.min(
|
|
11871
|
-
Math.floor(Math.random() * (maxChunkSize - minChunkSize + 1)) + minChunkSize,
|
|
11872
|
-
remaining.length
|
|
11873
|
-
);
|
|
11874
|
-
let chunk;
|
|
11875
|
-
if (chunkSize < remaining.length) {
|
|
11876
|
-
const substr = remaining.substring(0, chunkSize);
|
|
11877
|
-
const lastSpace = substr.lastIndexOf(" ");
|
|
11878
|
-
if (lastSpace > minChunkSize / 2) {
|
|
11879
|
-
chunk = substr.substring(0, lastSpace + 1);
|
|
11880
|
-
} else {
|
|
11881
|
-
chunk = substr;
|
|
11882
|
-
}
|
|
11883
|
-
} else {
|
|
11884
|
-
chunk = remaining;
|
|
11885
|
-
}
|
|
11886
|
-
chunks.push(chunk);
|
|
11887
|
-
remaining = remaining.substring(chunk.length);
|
|
11888
|
-
}
|
|
11889
|
-
return chunks;
|
|
11890
|
-
}
|
|
11891
|
-
function serializeToBlockFormat(obj, prefix = "") {
|
|
11892
|
-
let result = "";
|
|
11893
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
11894
|
-
const pointer = prefix ? `${prefix}/${key}` : key;
|
|
11895
|
-
if (value === null || value === void 0) {
|
|
11896
|
-
continue;
|
|
11897
|
-
}
|
|
11898
|
-
if (Array.isArray(value)) {
|
|
11899
|
-
for (let i = 0; i < value.length; i++) {
|
|
11900
|
-
const item = value[i];
|
|
11901
|
-
const itemPointer = `${pointer}/${i}`;
|
|
11902
|
-
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
11903
|
-
result += serializeToBlockFormat(item, itemPointer);
|
|
11904
|
-
} else if (Array.isArray(item)) {
|
|
11905
|
-
for (let j = 0; j < item.length; j++) {
|
|
11906
|
-
result += `${GADGET_ARG_PREFIX}${itemPointer}/${j}
|
|
11907
|
-
${String(item[j])}
|
|
11908
|
-
`;
|
|
11909
|
-
}
|
|
11910
|
-
} else {
|
|
11911
|
-
result += `${GADGET_ARG_PREFIX}${itemPointer}
|
|
11912
|
-
${String(item)}
|
|
11913
|
-
`;
|
|
11914
|
-
}
|
|
11915
|
-
}
|
|
11916
|
-
} else if (typeof value === "object") {
|
|
11917
|
-
result += serializeToBlockFormat(value, pointer);
|
|
11918
|
-
} else {
|
|
11919
|
-
result += `${GADGET_ARG_PREFIX}${pointer}
|
|
11920
|
-
${String(value)}
|
|
11921
|
-
`;
|
|
11922
|
-
}
|
|
11923
|
-
}
|
|
11924
|
-
return result;
|
|
11925
|
-
}
|
|
11926
|
-
function formatGadgetCalls(gadgetCalls) {
|
|
11927
|
-
let text3 = "";
|
|
11928
|
-
const calls = [];
|
|
11929
|
-
for (const call of gadgetCalls) {
|
|
11930
|
-
const invocationId = call.invocationId ?? generateInvocationId();
|
|
11931
|
-
calls.push({ name: call.gadgetName, invocationId });
|
|
11932
|
-
const blockParams = serializeToBlockFormat(call.parameters);
|
|
11933
|
-
text3 += `
|
|
11934
|
-
${GADGET_START_PREFIX}${call.gadgetName}
|
|
11935
|
-
${blockParams}${GADGET_END_PREFIX}`;
|
|
11936
|
-
}
|
|
11937
|
-
return { text: text3, calls };
|
|
11938
|
-
}
|
|
11939
|
-
async function* createMockStream(response) {
|
|
11940
|
-
if (response.delayMs) {
|
|
11941
|
-
await sleep(response.delayMs);
|
|
11942
|
-
}
|
|
11943
|
-
const streamDelay = response.streamDelayMs ?? 0;
|
|
11944
|
-
let fullText = response.text ?? "";
|
|
11945
|
-
if (response.gadgetCalls && response.gadgetCalls.length > 0) {
|
|
11946
|
-
const { text: gadgetText } = formatGadgetCalls(response.gadgetCalls);
|
|
11947
|
-
fullText += gadgetText;
|
|
11948
|
-
}
|
|
11949
|
-
if (fullText.length > 0) {
|
|
11950
|
-
const chunks = streamDelay > 0 ? splitIntoChunks(fullText) : [fullText];
|
|
11951
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
11952
|
-
const isLast = i === chunks.length - 1;
|
|
11953
|
-
const chunk = {
|
|
11954
|
-
text: chunks[i]
|
|
11955
|
-
};
|
|
11956
|
-
if (isLast) {
|
|
11957
|
-
if (response.finishReason !== void 0) {
|
|
11958
|
-
chunk.finishReason = response.finishReason;
|
|
11959
|
-
}
|
|
11960
|
-
if (response.usage) {
|
|
11961
|
-
chunk.usage = response.usage;
|
|
11962
|
-
}
|
|
11963
|
-
}
|
|
11964
|
-
yield chunk;
|
|
11965
|
-
if (streamDelay > 0 && !isLast) {
|
|
11966
|
-
await sleep(streamDelay);
|
|
11967
|
-
}
|
|
11968
|
-
}
|
|
11969
|
-
} else {
|
|
11970
|
-
yield {
|
|
11971
|
-
text: "",
|
|
11972
|
-
finishReason: response.finishReason ?? "stop",
|
|
11973
|
-
usage: response.usage ?? { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
|
|
11974
|
-
};
|
|
11975
|
-
}
|
|
11976
|
-
}
|
|
11977
|
-
function createTextMockStream(text3, options) {
|
|
11978
|
-
return createMockStream({
|
|
11979
|
-
text: text3,
|
|
11980
|
-
delayMs: options?.delayMs,
|
|
11981
|
-
streamDelayMs: options?.streamDelayMs,
|
|
11982
|
-
usage: options?.usage,
|
|
11983
|
-
finishReason: "stop"
|
|
11984
|
-
});
|
|
11985
|
-
}
|
|
11986
|
-
|
|
11987
|
-
// src/testing/mock-adapter.ts
|
|
11988
|
-
var MockProviderAdapter = class {
|
|
11989
|
-
providerId = "mock";
|
|
11990
|
-
priority = 100;
|
|
11991
|
-
// High priority: check mocks before real providers
|
|
11992
|
-
mockManager;
|
|
11993
|
-
constructor(options) {
|
|
11994
|
-
this.mockManager = getMockManager(options);
|
|
11995
|
-
}
|
|
11996
|
-
supports(_descriptor) {
|
|
11997
|
-
return true;
|
|
11998
|
-
}
|
|
11999
|
-
stream(options, descriptor, _spec) {
|
|
12000
|
-
const context = {
|
|
12001
|
-
model: options.model,
|
|
12002
|
-
provider: descriptor.provider,
|
|
12003
|
-
modelName: descriptor.name,
|
|
12004
|
-
options,
|
|
12005
|
-
messages: options.messages
|
|
12006
|
-
};
|
|
12007
|
-
return this.createMockStreamFromContext(context);
|
|
12008
|
-
}
|
|
12009
|
-
async *createMockStreamFromContext(context) {
|
|
12010
|
-
const mockResponse = await this.mockManager.findMatch(context);
|
|
12011
|
-
if (!mockResponse) {
|
|
12012
|
-
yield {
|
|
12013
|
-
text: "",
|
|
12014
|
-
finishReason: "stop",
|
|
12015
|
-
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
|
|
12016
|
-
};
|
|
12017
|
-
return;
|
|
12018
|
-
}
|
|
12019
|
-
yield* createMockStream(mockResponse);
|
|
12020
|
-
}
|
|
12021
|
-
// ==========================================================================
|
|
12022
|
-
// Image Generation Support
|
|
12023
|
-
// ==========================================================================
|
|
12024
|
-
/**
|
|
12025
|
-
* Check if this adapter supports image generation for a given model.
|
|
12026
|
-
* Returns true if there's a registered mock with images for this model.
|
|
12027
|
-
*/
|
|
12028
|
-
supportsImageGeneration(_modelId) {
|
|
12029
|
-
return true;
|
|
12030
|
-
}
|
|
12031
|
-
/**
|
|
12032
|
-
* Generate mock images based on registered mocks.
|
|
12033
|
-
*
|
|
12034
|
-
* @param options - Image generation options
|
|
12035
|
-
* @returns Mock image generation result
|
|
12036
|
-
*/
|
|
12037
|
-
async generateImage(options) {
|
|
12038
|
-
const context = {
|
|
12039
|
-
model: options.model,
|
|
12040
|
-
provider: "mock",
|
|
12041
|
-
modelName: options.model,
|
|
12042
|
-
options: {
|
|
12043
|
-
model: options.model,
|
|
12044
|
-
messages: [{ role: "user", content: options.prompt }]
|
|
12045
|
-
},
|
|
12046
|
-
messages: [{ role: "user", content: options.prompt }]
|
|
12047
|
-
};
|
|
12048
|
-
const mockResponse = await this.mockManager.findMatch(context);
|
|
12049
|
-
if (!mockResponse?.images || mockResponse.images.length === 0) {
|
|
12050
|
-
throw new Error(
|
|
12051
|
-
`No mock registered for image generation with model "${options.model}". Use mockLLM().forModel("${options.model}").returnsImage(...).register() to add one.`
|
|
12052
|
-
);
|
|
12053
|
-
}
|
|
12054
|
-
return this.createImageResult(options, mockResponse);
|
|
12055
|
-
}
|
|
12056
|
-
/**
|
|
12057
|
-
* Transform mock response into ImageGenerationResult format.
|
|
12058
|
-
*
|
|
12059
|
-
* @param options - Original image generation options
|
|
12060
|
-
* @param mockResponse - Mock response containing image data
|
|
12061
|
-
* @returns ImageGenerationResult with mock data and zero cost
|
|
12062
|
-
*/
|
|
12063
|
-
createImageResult(options, mockResponse) {
|
|
12064
|
-
const images = mockResponse.images ?? [];
|
|
12065
|
-
return {
|
|
12066
|
-
images: images.map((img) => ({
|
|
12067
|
-
b64Json: img.data,
|
|
12068
|
-
revisedPrompt: img.revisedPrompt
|
|
12069
|
-
})),
|
|
12070
|
-
model: options.model,
|
|
12071
|
-
usage: {
|
|
12072
|
-
imagesGenerated: images.length,
|
|
12073
|
-
size: options.size ?? "1024x1024",
|
|
12074
|
-
quality: options.quality ?? "standard"
|
|
12075
|
-
},
|
|
12076
|
-
cost: 0
|
|
12077
|
-
// Mock cost is always 0
|
|
12078
|
-
};
|
|
12079
|
-
}
|
|
12080
|
-
// ==========================================================================
|
|
12081
|
-
// Speech Generation Support
|
|
12082
|
-
// ==========================================================================
|
|
12083
|
-
/**
|
|
12084
|
-
* Check if this adapter supports speech generation for a given model.
|
|
12085
|
-
* Returns true if there's a registered mock with audio for this model.
|
|
12086
|
-
*/
|
|
12087
|
-
supportsSpeechGeneration(_modelId) {
|
|
12088
|
-
return true;
|
|
12089
|
-
}
|
|
12090
|
-
/**
|
|
12091
|
-
* Generate mock speech based on registered mocks.
|
|
12092
|
-
*
|
|
12093
|
-
* @param options - Speech generation options
|
|
12094
|
-
* @returns Mock speech generation result
|
|
12095
|
-
*/
|
|
12096
|
-
async generateSpeech(options) {
|
|
12097
|
-
const context = {
|
|
12098
|
-
model: options.model,
|
|
12099
|
-
provider: "mock",
|
|
12100
|
-
modelName: options.model,
|
|
12101
|
-
options: {
|
|
12102
|
-
model: options.model,
|
|
12103
|
-
messages: [{ role: "user", content: options.input }]
|
|
12104
|
-
},
|
|
12105
|
-
messages: [{ role: "user", content: options.input }]
|
|
12106
|
-
};
|
|
12107
|
-
const mockResponse = await this.mockManager.findMatch(context);
|
|
12108
|
-
if (!mockResponse?.audio) {
|
|
12109
|
-
throw new Error(
|
|
12110
|
-
`No mock registered for speech generation with model "${options.model}". Use mockLLM().forModel("${options.model}").returnsAudio(...).register() to add one.`
|
|
12111
|
-
);
|
|
12112
|
-
}
|
|
12113
|
-
return this.createSpeechResult(options, mockResponse);
|
|
12114
|
-
}
|
|
12115
|
-
/**
|
|
12116
|
-
* Transform mock response into SpeechGenerationResult format.
|
|
12117
|
-
* Converts base64 audio data to ArrayBuffer.
|
|
12118
|
-
*
|
|
12119
|
-
* @param options - Original speech generation options
|
|
12120
|
-
* @param mockResponse - Mock response containing audio data
|
|
12121
|
-
* @returns SpeechGenerationResult with mock data and zero cost
|
|
12122
|
-
*/
|
|
12123
|
-
createSpeechResult(options, mockResponse) {
|
|
12124
|
-
const audio = mockResponse.audio;
|
|
12125
|
-
const binaryString = atob(audio.data);
|
|
12126
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
12127
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
12128
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
12129
|
-
}
|
|
12130
|
-
const format = this.mimeTypeToAudioFormat(audio.mimeType);
|
|
12131
|
-
return {
|
|
12132
|
-
audio: bytes.buffer,
|
|
12133
|
-
model: options.model,
|
|
12134
|
-
usage: {
|
|
12135
|
-
characterCount: options.input.length
|
|
12136
|
-
},
|
|
12137
|
-
cost: 0,
|
|
12138
|
-
// Mock cost is always 0
|
|
12139
|
-
format
|
|
12140
|
-
};
|
|
12141
|
-
}
|
|
12142
|
-
/**
|
|
12143
|
-
* Map MIME type to audio format for SpeechGenerationResult.
|
|
12144
|
-
* Defaults to "mp3" for unknown MIME types.
|
|
12145
|
-
*
|
|
12146
|
-
* @param mimeType - Audio MIME type string
|
|
12147
|
-
* @returns Audio format identifier
|
|
12148
|
-
*/
|
|
12149
|
-
mimeTypeToAudioFormat(mimeType) {
|
|
12150
|
-
const mapping = {
|
|
12151
|
-
"audio/mp3": "mp3",
|
|
12152
|
-
"audio/mpeg": "mp3",
|
|
12153
|
-
"audio/wav": "wav",
|
|
12154
|
-
"audio/webm": "opus",
|
|
12155
|
-
"audio/ogg": "opus"
|
|
12156
|
-
};
|
|
12157
|
-
return mapping[mimeType] ?? "mp3";
|
|
12158
|
-
}
|
|
12159
|
-
};
|
|
12160
|
-
function createMockAdapter(options) {
|
|
12161
|
-
return new MockProviderAdapter(options);
|
|
12162
|
-
}
|
|
12163
|
-
|
|
12164
|
-
// src/testing/mock-builder.ts
|
|
12165
|
-
init_input_content();
|
|
12166
|
-
init_messages();
|
|
12167
|
-
function hasImageContent(content) {
|
|
12168
|
-
if (typeof content === "string") return false;
|
|
12169
|
-
return content.some((part) => isImagePart(part));
|
|
12170
|
-
}
|
|
12171
|
-
function hasAudioContent(content) {
|
|
12172
|
-
if (typeof content === "string") return false;
|
|
12173
|
-
return content.some((part) => isAudioPart(part));
|
|
12174
|
-
}
|
|
12175
|
-
function countImages(content) {
|
|
12176
|
-
if (typeof content === "string") return 0;
|
|
12177
|
-
return content.filter((part) => isImagePart(part)).length;
|
|
12178
|
-
}
|
|
12179
|
-
var MockBuilder = class {
|
|
12180
|
-
matchers = [];
|
|
12181
|
-
response = {};
|
|
12182
|
-
label;
|
|
12183
|
-
isOnce = false;
|
|
12184
|
-
id;
|
|
12185
|
-
/**
|
|
12186
|
-
* Match calls to a specific model (by name, supports partial matching).
|
|
12187
|
-
*
|
|
12188
|
-
* @example
|
|
12189
|
-
* mockLLM().forModel('gpt-5')
|
|
12190
|
-
* mockLLM().forModel('claude') // matches any Claude model
|
|
12191
|
-
*/
|
|
12192
|
-
forModel(modelName) {
|
|
12193
|
-
if (!modelName || modelName.trim() === "") {
|
|
12194
|
-
throw new Error("Model name cannot be empty");
|
|
12195
|
-
}
|
|
12196
|
-
this.matchers.push((ctx) => ctx.modelName.includes(modelName));
|
|
12197
|
-
return this;
|
|
12198
|
-
}
|
|
12199
|
-
/**
|
|
12200
|
-
* Match calls to any model.
|
|
12201
|
-
* Useful when you want to mock responses regardless of the model used.
|
|
12202
|
-
*
|
|
12203
|
-
* @example
|
|
12204
|
-
* mockLLM().forAnyModel()
|
|
12205
|
-
*/
|
|
12206
|
-
forAnyModel() {
|
|
12207
|
-
this.matchers.push(() => true);
|
|
12208
|
-
return this;
|
|
12209
|
-
}
|
|
12210
|
-
/**
|
|
12211
|
-
* Match calls to a specific provider.
|
|
12212
|
-
*
|
|
12213
|
-
* @example
|
|
12214
|
-
* mockLLM().forProvider('openai')
|
|
12215
|
-
* mockLLM().forProvider('anthropic')
|
|
12216
|
-
*/
|
|
12217
|
-
forProvider(provider) {
|
|
12218
|
-
if (!provider || provider.trim() === "") {
|
|
12219
|
-
throw new Error("Provider name cannot be empty");
|
|
12220
|
-
}
|
|
12221
|
-
this.matchers.push((ctx) => ctx.provider === provider);
|
|
12222
|
-
return this;
|
|
12223
|
-
}
|
|
12224
|
-
/**
|
|
12225
|
-
* Match calls to any provider.
|
|
12226
|
-
* Useful when you want to mock responses regardless of the provider used.
|
|
12227
|
-
*
|
|
12228
|
-
* @example
|
|
12229
|
-
* mockLLM().forAnyProvider()
|
|
12230
|
-
*/
|
|
12231
|
-
forAnyProvider() {
|
|
12232
|
-
this.matchers.push(() => true);
|
|
12233
|
-
return this;
|
|
12234
|
-
}
|
|
12235
|
-
/**
|
|
12236
|
-
* Match when any message contains the given text (case-insensitive).
|
|
12237
|
-
*
|
|
12238
|
-
* @example
|
|
12239
|
-
* mockLLM().whenMessageContains('hello')
|
|
12240
|
-
*/
|
|
12241
|
-
whenMessageContains(text3) {
|
|
12242
|
-
this.matchers.push(
|
|
12243
|
-
(ctx) => ctx.messages.some(
|
|
12244
|
-
(msg) => extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
12245
|
-
)
|
|
12246
|
-
);
|
|
12247
|
-
return this;
|
|
12248
|
-
}
|
|
12249
|
-
/**
|
|
12250
|
-
* Match when the last message contains the given text (case-insensitive).
|
|
12251
|
-
*
|
|
12252
|
-
* @example
|
|
12253
|
-
* mockLLM().whenLastMessageContains('goodbye')
|
|
12254
|
-
*/
|
|
12255
|
-
whenLastMessageContains(text3) {
|
|
12256
|
-
this.matchers.push((ctx) => {
|
|
12257
|
-
const lastMsg = ctx.messages[ctx.messages.length - 1];
|
|
12258
|
-
if (!lastMsg) return false;
|
|
12259
|
-
return extractMessageText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
|
|
12260
|
-
});
|
|
12261
|
-
return this;
|
|
12262
|
-
}
|
|
12263
|
-
/**
|
|
12264
|
-
* Match when any message matches the given regex.
|
|
12265
|
-
*
|
|
12266
|
-
* @example
|
|
12267
|
-
* mockLLM().whenMessageMatches(/calculate \d+/)
|
|
12268
|
-
*/
|
|
12269
|
-
whenMessageMatches(regex) {
|
|
12270
|
-
this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractMessageText(msg.content))));
|
|
12271
|
-
return this;
|
|
12272
|
-
}
|
|
12273
|
-
/**
|
|
12274
|
-
* Match when a message with a specific role contains text.
|
|
12275
|
-
*
|
|
12276
|
-
* @example
|
|
12277
|
-
* mockLLM().whenRoleContains('system', 'You are a helpful assistant')
|
|
12278
|
-
*/
|
|
12279
|
-
whenRoleContains(role, text3) {
|
|
12280
|
-
this.matchers.push(
|
|
12281
|
-
(ctx) => ctx.messages.some(
|
|
12282
|
-
(msg) => msg.role === role && extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
12283
|
-
)
|
|
12284
|
-
);
|
|
12285
|
-
return this;
|
|
12286
|
-
}
|
|
12287
|
-
/**
|
|
12288
|
-
* Match based on the number of messages in the conversation.
|
|
12289
|
-
*
|
|
12290
|
-
* @example
|
|
12291
|
-
* mockLLM().whenMessageCount((count) => count > 10)
|
|
12292
|
-
*/
|
|
12293
|
-
whenMessageCount(predicate) {
|
|
12294
|
-
this.matchers.push((ctx) => predicate(ctx.messages.length));
|
|
12295
|
-
return this;
|
|
12296
|
-
}
|
|
12297
|
-
/**
|
|
12298
|
-
* Add a custom matcher function.
|
|
12299
|
-
* This provides full control over matching logic.
|
|
12300
|
-
*
|
|
12301
|
-
* @example
|
|
12302
|
-
* mockLLM().when((ctx) => {
|
|
12303
|
-
* return ctx.options.temperature > 0.8;
|
|
12304
|
-
* })
|
|
12305
|
-
*/
|
|
12306
|
-
when(matcher) {
|
|
12307
|
-
this.matchers.push(matcher);
|
|
12308
|
-
return this;
|
|
12309
|
-
}
|
|
12310
|
-
// ==========================================================================
|
|
12311
|
-
// Multimodal Matchers
|
|
12312
|
-
// ==========================================================================
|
|
12313
|
-
/**
|
|
12314
|
-
* Match when any message contains an image.
|
|
12315
|
-
*
|
|
12316
|
-
* @example
|
|
12317
|
-
* mockLLM().whenMessageHasImage().returns("I see an image of a sunset.")
|
|
12318
|
-
*/
|
|
12319
|
-
whenMessageHasImage() {
|
|
12320
|
-
this.matchers.push((ctx) => ctx.messages.some((msg) => hasImageContent(msg.content)));
|
|
12321
|
-
return this;
|
|
12322
|
-
}
|
|
12323
|
-
/**
|
|
12324
|
-
* Match when any message contains audio.
|
|
12325
|
-
*
|
|
12326
|
-
* @example
|
|
12327
|
-
* mockLLM().whenMessageHasAudio().returns("I hear music playing.")
|
|
12328
|
-
*/
|
|
12329
|
-
whenMessageHasAudio() {
|
|
12330
|
-
this.matchers.push((ctx) => ctx.messages.some((msg) => hasAudioContent(msg.content)));
|
|
12331
|
-
return this;
|
|
12332
|
-
}
|
|
12333
|
-
/**
|
|
12334
|
-
* Match based on the number of images in the last message.
|
|
12335
|
-
*
|
|
12336
|
-
* @example
|
|
12337
|
-
* mockLLM().whenImageCount((n) => n >= 2).returns("Comparing multiple images...")
|
|
12338
|
-
*/
|
|
12339
|
-
whenImageCount(predicate) {
|
|
12340
|
-
this.matchers.push((ctx) => {
|
|
12341
|
-
const lastMsg = ctx.messages[ctx.messages.length - 1];
|
|
12342
|
-
if (!lastMsg) return false;
|
|
12343
|
-
return predicate(countImages(lastMsg.content));
|
|
12344
|
-
});
|
|
12345
|
-
return this;
|
|
12346
|
-
}
|
|
12347
|
-
/**
|
|
12348
|
-
* Set the text response to return.
|
|
12349
|
-
* Can be a static string or a function that returns a string dynamically.
|
|
12350
|
-
*
|
|
12351
|
-
* @example
|
|
12352
|
-
* mockLLM().returns('Hello, world!')
|
|
12353
|
-
* mockLLM().returns(() => `Response at ${Date.now()}`)
|
|
12354
|
-
* mockLLM().returns((ctx) => `You said: ${ctx.messages[0]?.content}`)
|
|
12355
|
-
*/
|
|
12356
|
-
returns(text3) {
|
|
12357
|
-
if (typeof text3 === "function") {
|
|
12358
|
-
this.response = async (ctx) => {
|
|
12359
|
-
const resolvedText = await Promise.resolve().then(() => text3(ctx));
|
|
12360
|
-
return { text: resolvedText };
|
|
12361
|
-
};
|
|
12362
|
-
} else {
|
|
12363
|
-
if (typeof this.response === "function") {
|
|
12364
|
-
throw new Error("Cannot use returns() after withResponse() with a function");
|
|
12365
|
-
}
|
|
12366
|
-
this.response.text = text3;
|
|
12367
|
-
}
|
|
12368
|
-
return this;
|
|
12369
|
-
}
|
|
12370
|
-
/**
|
|
12371
|
-
* Set gadget calls to include in the response.
|
|
12372
|
-
*
|
|
12373
|
-
* @example
|
|
12374
|
-
* mockLLM().returnsGadgetCalls([
|
|
12375
|
-
* { gadgetName: 'calculator', parameters: { op: 'add', a: 1, b: 2 } }
|
|
12376
|
-
* ])
|
|
12377
|
-
*/
|
|
12378
|
-
returnsGadgetCalls(calls) {
|
|
12379
|
-
if (typeof this.response === "function") {
|
|
12380
|
-
throw new Error("Cannot use returnsGadgetCalls() after withResponse() with a function");
|
|
12381
|
-
}
|
|
12382
|
-
this.response.gadgetCalls = calls;
|
|
12383
|
-
return this;
|
|
12384
|
-
}
|
|
12385
|
-
/**
|
|
12386
|
-
* Add a single gadget call to the response.
|
|
12387
|
-
*
|
|
12388
|
-
* @example
|
|
12389
|
-
* mockLLM()
|
|
12390
|
-
* .returnsGadgetCall('calculator', { op: 'add', a: 1, b: 2 })
|
|
12391
|
-
* .returnsGadgetCall('logger', { message: 'Done!' })
|
|
12392
|
-
*/
|
|
12393
|
-
returnsGadgetCall(gadgetName, parameters) {
|
|
12394
|
-
if (typeof this.response === "function") {
|
|
12395
|
-
throw new Error("Cannot use returnsGadgetCall() after withResponse() with a function");
|
|
12396
|
-
}
|
|
12397
|
-
if (!this.response.gadgetCalls) {
|
|
12398
|
-
this.response.gadgetCalls = [];
|
|
12399
|
-
}
|
|
12400
|
-
this.response.gadgetCalls.push({ gadgetName, parameters });
|
|
12401
|
-
return this;
|
|
12402
|
-
}
|
|
12403
|
-
// ==========================================================================
|
|
12404
|
-
// Multimodal Response Helpers
|
|
12405
|
-
// ==========================================================================
|
|
12406
|
-
/**
|
|
12407
|
-
* Return a single image in the response.
|
|
12408
|
-
* Useful for mocking image generation endpoints.
|
|
12409
|
-
*
|
|
12410
|
-
* @param data - Image data (base64 string or Buffer)
|
|
12411
|
-
* @param mimeType - MIME type (auto-detected if Buffer provided without type)
|
|
12412
|
-
*
|
|
12413
|
-
* @example
|
|
12414
|
-
* mockLLM()
|
|
12415
|
-
* .forModel('dall-e-3')
|
|
12416
|
-
* .returnsImage(pngBuffer)
|
|
12417
|
-
* .register();
|
|
12418
|
-
*/
|
|
12419
|
-
returnsImage(data, mimeType) {
|
|
12420
|
-
if (typeof this.response === "function") {
|
|
12421
|
-
throw new Error("Cannot use returnsImage() after withResponse() with a function");
|
|
12422
|
-
}
|
|
12423
|
-
let imageData;
|
|
12424
|
-
let imageMime;
|
|
12425
|
-
if (typeof data === "string") {
|
|
12426
|
-
imageData = data;
|
|
12427
|
-
if (!mimeType) {
|
|
12428
|
-
throw new Error("MIME type is required when providing base64 string data");
|
|
12429
|
-
}
|
|
12430
|
-
imageMime = mimeType;
|
|
12431
|
-
} else {
|
|
12432
|
-
imageData = toBase64(data);
|
|
12433
|
-
const detected = mimeType ?? detectImageMimeType(data);
|
|
12434
|
-
if (!detected) {
|
|
12435
|
-
throw new Error(
|
|
12436
|
-
"Could not detect image MIME type. Please provide the mimeType parameter explicitly."
|
|
12437
|
-
);
|
|
12438
|
-
}
|
|
12439
|
-
imageMime = detected;
|
|
12440
|
-
}
|
|
12441
|
-
if (!this.response.images) {
|
|
12442
|
-
this.response.images = [];
|
|
12443
|
-
}
|
|
12444
|
-
this.response.images.push({ data: imageData, mimeType: imageMime });
|
|
12445
|
-
return this;
|
|
12446
|
-
}
|
|
12447
|
-
/**
|
|
12448
|
-
* Return multiple images in the response.
|
|
12449
|
-
*
|
|
12450
|
-
* @example
|
|
12451
|
-
* mockLLM()
|
|
12452
|
-
* .forModel('dall-e-3')
|
|
12453
|
-
* .returnsImages([
|
|
12454
|
-
* { data: pngBuffer1 },
|
|
12455
|
-
* { data: pngBuffer2 },
|
|
12456
|
-
* ])
|
|
12457
|
-
* .register();
|
|
12458
|
-
*/
|
|
12459
|
-
returnsImages(images) {
|
|
12460
|
-
for (const img of images) {
|
|
12461
|
-
this.returnsImage(img.data, img.mimeType);
|
|
12462
|
-
if (img.revisedPrompt && this.response && typeof this.response !== "function") {
|
|
12463
|
-
const lastImage = this.response.images?.[this.response.images.length - 1];
|
|
12464
|
-
if (lastImage) {
|
|
12465
|
-
lastImage.revisedPrompt = img.revisedPrompt;
|
|
12466
|
-
}
|
|
12467
|
-
}
|
|
12468
|
-
}
|
|
12469
|
-
return this;
|
|
12470
|
-
}
|
|
12471
|
-
/**
|
|
12472
|
-
* Return audio data in the response.
|
|
12473
|
-
* Useful for mocking speech synthesis endpoints.
|
|
12474
|
-
*
|
|
12475
|
-
* @param data - Audio data (base64 string or Buffer)
|
|
12476
|
-
* @param mimeType - MIME type (auto-detected if Buffer provided without type)
|
|
12477
|
-
*
|
|
12478
|
-
* @example
|
|
12479
|
-
* mockLLM()
|
|
12480
|
-
* .forModel('tts-1')
|
|
12481
|
-
* .returnsAudio(mp3Buffer)
|
|
12482
|
-
* .register();
|
|
12483
|
-
*/
|
|
12484
|
-
returnsAudio(data, mimeType) {
|
|
12485
|
-
if (typeof this.response === "function") {
|
|
12486
|
-
throw new Error("Cannot use returnsAudio() after withResponse() with a function");
|
|
12487
|
-
}
|
|
12488
|
-
let audioData;
|
|
12489
|
-
let audioMime;
|
|
12490
|
-
if (typeof data === "string") {
|
|
12491
|
-
audioData = data;
|
|
12492
|
-
if (!mimeType) {
|
|
12493
|
-
throw new Error("MIME type is required when providing base64 string data");
|
|
12494
|
-
}
|
|
12495
|
-
audioMime = mimeType;
|
|
12496
|
-
} else {
|
|
12497
|
-
audioData = toBase64(data);
|
|
12498
|
-
const detected = mimeType ?? detectAudioMimeType(data);
|
|
12499
|
-
if (!detected) {
|
|
12500
|
-
throw new Error(
|
|
12501
|
-
"Could not detect audio MIME type. Please provide the mimeType parameter explicitly."
|
|
12502
|
-
);
|
|
12503
|
-
}
|
|
12504
|
-
audioMime = detected;
|
|
12505
|
-
}
|
|
12506
|
-
this.response.audio = { data: audioData, mimeType: audioMime };
|
|
12507
|
-
return this;
|
|
12508
|
-
}
|
|
12509
|
-
/**
|
|
12510
|
-
* Set the complete mock response object.
|
|
12511
|
-
* This allows full control over all response properties.
|
|
12512
|
-
* Can also be a function that generates the response dynamically based on context.
|
|
12513
|
-
*
|
|
12514
|
-
* @example
|
|
12515
|
-
* // Static response
|
|
12516
|
-
* mockLLM().withResponse({
|
|
12517
|
-
* text: 'Hello',
|
|
12518
|
-
* usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
12519
|
-
* finishReason: 'stop'
|
|
12520
|
-
* })
|
|
12521
|
-
*
|
|
12522
|
-
* @example
|
|
12523
|
-
* // Dynamic response
|
|
12524
|
-
* mockLLM().withResponse((ctx) => ({
|
|
12525
|
-
* text: `You said: ${ctx.messages[ctx.messages.length - 1]?.content}`,
|
|
12526
|
-
* usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 }
|
|
12527
|
-
* }))
|
|
12528
|
-
*/
|
|
12529
|
-
withResponse(response) {
|
|
12530
|
-
this.response = response;
|
|
12531
|
-
return this;
|
|
12532
|
-
}
|
|
12533
|
-
/**
|
|
12534
|
-
* Set simulated token usage.
|
|
12535
|
-
*
|
|
12536
|
-
* @example
|
|
12537
|
-
* mockLLM().withUsage({ inputTokens: 100, outputTokens: 50, totalTokens: 150 })
|
|
12538
|
-
*/
|
|
12539
|
-
withUsage(usage) {
|
|
12540
|
-
if (typeof this.response === "function") {
|
|
12541
|
-
throw new Error("Cannot use withUsage() after withResponse() with a function");
|
|
12542
|
-
}
|
|
12543
|
-
if (usage.inputTokens < 0 || usage.outputTokens < 0 || usage.totalTokens < 0) {
|
|
12544
|
-
throw new Error("Token counts cannot be negative");
|
|
12545
|
-
}
|
|
12546
|
-
if (usage.totalTokens !== usage.inputTokens + usage.outputTokens) {
|
|
12547
|
-
throw new Error("totalTokens must equal inputTokens + outputTokens");
|
|
12548
|
-
}
|
|
12549
|
-
this.response.usage = usage;
|
|
12550
|
-
return this;
|
|
12551
|
-
}
|
|
12552
|
-
/**
|
|
12553
|
-
* Set the finish reason.
|
|
12554
|
-
*
|
|
12555
|
-
* @example
|
|
12556
|
-
* mockLLM().withFinishReason('stop')
|
|
12557
|
-
* mockLLM().withFinishReason('length')
|
|
12558
|
-
*/
|
|
12559
|
-
withFinishReason(reason) {
|
|
12560
|
-
if (typeof this.response === "function") {
|
|
12561
|
-
throw new Error("Cannot use withFinishReason() after withResponse() with a function");
|
|
12562
|
-
}
|
|
12563
|
-
this.response.finishReason = reason;
|
|
12564
|
-
return this;
|
|
12565
|
-
}
|
|
12566
|
-
/**
|
|
12567
|
-
* Set initial delay before streaming starts (simulates network latency).
|
|
12568
|
-
*
|
|
12569
|
-
* @example
|
|
12570
|
-
* mockLLM().withDelay(100) // 100ms delay
|
|
12571
|
-
*/
|
|
12572
|
-
withDelay(ms) {
|
|
12573
|
-
if (typeof this.response === "function") {
|
|
12574
|
-
throw new Error("Cannot use withDelay() after withResponse() with a function");
|
|
12575
|
-
}
|
|
12576
|
-
if (ms < 0) {
|
|
12577
|
-
throw new Error("Delay must be non-negative");
|
|
12578
|
-
}
|
|
12579
|
-
this.response.delayMs = ms;
|
|
12580
|
-
return this;
|
|
12581
|
-
}
|
|
12582
|
-
/**
|
|
12583
|
-
* Set delay between stream chunks (simulates realistic streaming).
|
|
12584
|
-
*
|
|
12585
|
-
* @example
|
|
12586
|
-
* mockLLM().withStreamDelay(10) // 10ms between chunks
|
|
12587
|
-
*/
|
|
12588
|
-
withStreamDelay(ms) {
|
|
12589
|
-
if (typeof this.response === "function") {
|
|
12590
|
-
throw new Error("Cannot use withStreamDelay() after withResponse() with a function");
|
|
12591
|
-
}
|
|
12592
|
-
if (ms < 0) {
|
|
12593
|
-
throw new Error("Stream delay must be non-negative");
|
|
12594
|
-
}
|
|
12595
|
-
this.response.streamDelayMs = ms;
|
|
12596
|
-
return this;
|
|
12597
|
-
}
|
|
12598
|
-
/**
|
|
12599
|
-
* Set a label for this mock (useful for debugging).
|
|
12600
|
-
*
|
|
12601
|
-
* @example
|
|
12602
|
-
* mockLLM().withLabel('greeting mock')
|
|
12603
|
-
*/
|
|
12604
|
-
withLabel(label) {
|
|
12605
|
-
this.label = label;
|
|
12606
|
-
return this;
|
|
12607
|
-
}
|
|
12608
|
-
/**
|
|
12609
|
-
* Set a specific ID for this mock.
|
|
12610
|
-
*
|
|
12611
|
-
* @example
|
|
12612
|
-
* mockLLM().withId('my-custom-mock-id')
|
|
12613
|
-
*/
|
|
12614
|
-
withId(id) {
|
|
12615
|
-
this.id = id;
|
|
12616
|
-
return this;
|
|
12617
|
-
}
|
|
12618
|
-
/**
|
|
12619
|
-
* Mark this mock as one-time use (will be removed after first match).
|
|
12620
|
-
*
|
|
12621
|
-
* @example
|
|
12622
|
-
* mockLLM().once()
|
|
12623
|
-
*/
|
|
12624
|
-
once() {
|
|
12625
|
-
this.isOnce = true;
|
|
12626
|
-
return this;
|
|
12627
|
-
}
|
|
12628
|
-
/**
|
|
12629
|
-
* Build the mock registration without registering it.
|
|
12630
|
-
* Useful if you want to register it manually later.
|
|
12631
|
-
*
|
|
12632
|
-
* @returns The built MockRegistration object (without id if not specified)
|
|
12633
|
-
*/
|
|
12634
|
-
build() {
|
|
12635
|
-
if (this.matchers.length === 0) {
|
|
12636
|
-
throw new Error(
|
|
12637
|
-
"Mock must have at least one matcher. Use .when(), .forModel(), .forProvider(), etc."
|
|
12638
|
-
);
|
|
12639
|
-
}
|
|
12640
|
-
const combinedMatcher = async (ctx) => {
|
|
12641
|
-
for (const matcher of this.matchers) {
|
|
12642
|
-
const matches = await Promise.resolve(matcher(ctx));
|
|
12643
|
-
if (!matches) return false;
|
|
12644
|
-
}
|
|
12645
|
-
return true;
|
|
12646
|
-
};
|
|
12647
|
-
return {
|
|
12648
|
-
id: this.id,
|
|
12649
|
-
matcher: combinedMatcher,
|
|
12650
|
-
response: this.response,
|
|
12651
|
-
label: this.label,
|
|
12652
|
-
once: this.isOnce
|
|
12653
|
-
};
|
|
12654
|
-
}
|
|
12655
|
-
/**
|
|
12656
|
-
* Register this mock with the global MockManager.
|
|
12657
|
-
* Returns the ID of the registered mock.
|
|
12658
|
-
*
|
|
12659
|
-
* @example
|
|
12660
|
-
* const mockId = mockLLM().forModel('gpt-5').returns('Hello!').register();
|
|
12661
|
-
* // Later: getMockManager().unregister(mockId);
|
|
12662
|
-
*/
|
|
12663
|
-
register() {
|
|
12664
|
-
const mockManager = getMockManager();
|
|
12665
|
-
const registration = this.build();
|
|
12666
|
-
return mockManager.register(registration);
|
|
12667
|
-
}
|
|
12668
|
-
};
|
|
12669
|
-
function mockLLM() {
|
|
12670
|
-
return new MockBuilder();
|
|
12671
|
-
}
|
|
12672
|
-
|
|
12673
|
-
// src/testing/mock-client.ts
|
|
12674
|
-
init_client();
|
|
12675
|
-
function createMockClient(options) {
|
|
12676
|
-
return new LLMist({
|
|
12677
|
-
adapters: [new MockProviderAdapter(options)],
|
|
12678
|
-
autoDiscoverProviders: false,
|
|
12679
|
-
defaultProvider: "mock"
|
|
12680
|
-
});
|
|
12681
|
-
}
|
|
12682
|
-
|
|
12683
|
-
// src/testing/mock-gadget.ts
|
|
12684
|
-
init_gadget();
|
|
12685
|
-
|
|
12686
|
-
// src/index.ts
|
|
12687
12056
|
function getHostExports(ctx) {
|
|
12688
12057
|
if (!ctx?.hostExports) {
|
|
12689
12058
|
throw new Error(
|
|
@@ -12696,6 +12065,7 @@ function getHostExports(ctx) {
|
|
|
12696
12065
|
0 && (module.exports = {
|
|
12697
12066
|
AbortException,
|
|
12698
12067
|
AbstractGadget,
|
|
12068
|
+
Agent,
|
|
12699
12069
|
AgentBuilder,
|
|
12700
12070
|
AnthropicMessagesProvider,
|
|
12701
12071
|
CompactionManager,
|
|
@@ -12703,8 +12073,13 @@ function getHostExports(ctx) {
|
|
|
12703
12073
|
DEFAULT_COMPACTION_CONFIG,
|
|
12704
12074
|
DEFAULT_HINTS,
|
|
12705
12075
|
DEFAULT_PROMPTS,
|
|
12076
|
+
DEFAULT_RETRY_CONFIG,
|
|
12706
12077
|
DEFAULT_SUMMARIZATION_PROMPT,
|
|
12707
12078
|
ExecutionTree,
|
|
12079
|
+
FALLBACK_CHARS_PER_TOKEN,
|
|
12080
|
+
GADGET_ARG_PREFIX,
|
|
12081
|
+
GADGET_END_PREFIX,
|
|
12082
|
+
GADGET_START_PREFIX,
|
|
12708
12083
|
Gadget,
|
|
12709
12084
|
GadgetCallParser,
|
|
12710
12085
|
GadgetExecutor,
|
|
@@ -12718,9 +12093,6 @@ function getHostExports(ctx) {
|
|
|
12718
12093
|
LLMist,
|
|
12719
12094
|
MODEL_ALIASES,
|
|
12720
12095
|
MediaStore,
|
|
12721
|
-
MockBuilder,
|
|
12722
|
-
MockManager,
|
|
12723
|
-
MockProviderAdapter,
|
|
12724
12096
|
ModelIdentifierParser,
|
|
12725
12097
|
ModelRegistry,
|
|
12726
12098
|
OpenAIChatProvider,
|
|
@@ -12741,11 +12113,7 @@ function getHostExports(ctx) {
|
|
|
12741
12113
|
createHints,
|
|
12742
12114
|
createLogger,
|
|
12743
12115
|
createMediaOutput,
|
|
12744
|
-
createMockAdapter,
|
|
12745
|
-
createMockClient,
|
|
12746
|
-
createMockStream,
|
|
12747
12116
|
createOpenAIProviderFromEnv,
|
|
12748
|
-
createTextMockStream,
|
|
12749
12117
|
defaultLogger,
|
|
12750
12118
|
detectAudioMimeType,
|
|
12751
12119
|
detectImageMimeType,
|
|
@@ -12754,8 +12122,8 @@ function getHostExports(ctx) {
|
|
|
12754
12122
|
filterByDepth,
|
|
12755
12123
|
filterByParent,
|
|
12756
12124
|
filterRootEvents,
|
|
12125
|
+
formatLLMError,
|
|
12757
12126
|
getHostExports,
|
|
12758
|
-
getMockManager,
|
|
12759
12127
|
getModelId,
|
|
12760
12128
|
getProvider,
|
|
12761
12129
|
groupByParent,
|
|
@@ -12763,16 +12131,17 @@ function getHostExports(ctx) {
|
|
|
12763
12131
|
imageFromBase64,
|
|
12764
12132
|
imageFromBuffer,
|
|
12765
12133
|
imageFromUrl,
|
|
12134
|
+
isAbortError,
|
|
12766
12135
|
isAudioPart,
|
|
12767
12136
|
isDataUrl,
|
|
12768
12137
|
isGadgetEvent,
|
|
12769
12138
|
isImagePart,
|
|
12770
12139
|
isLLMEvent,
|
|
12140
|
+
isRetryableError,
|
|
12771
12141
|
isRootEvent,
|
|
12772
12142
|
isSubagentEvent,
|
|
12773
12143
|
isTextPart,
|
|
12774
12144
|
iterationProgressHint,
|
|
12775
|
-
mockLLM,
|
|
12776
12145
|
normalizeMessageContent,
|
|
12777
12146
|
parallelGadgetHint,
|
|
12778
12147
|
parseDataUrl,
|
|
@@ -12780,6 +12149,7 @@ function getHostExports(ctx) {
|
|
|
12780
12149
|
resolveHintTemplate,
|
|
12781
12150
|
resolveModel,
|
|
12782
12151
|
resolvePromptTemplate,
|
|
12152
|
+
resolveRetryConfig,
|
|
12783
12153
|
resolveRulesTemplate,
|
|
12784
12154
|
resolveSubagentModel,
|
|
12785
12155
|
resolveValue,
|
|
@@ -12789,11 +12159,13 @@ function getHostExports(ctx) {
|
|
|
12789
12159
|
resultWithImages,
|
|
12790
12160
|
resultWithMedia,
|
|
12791
12161
|
runWithHandlers,
|
|
12162
|
+
schemaToJSONSchema,
|
|
12792
12163
|
stream,
|
|
12793
12164
|
text,
|
|
12794
12165
|
toBase64,
|
|
12795
12166
|
validateAndApplyDefaults,
|
|
12796
12167
|
validateGadgetParams,
|
|
12168
|
+
validateGadgetSchema,
|
|
12797
12169
|
z
|
|
12798
12170
|
});
|
|
12799
12171
|
//# sourceMappingURL=index.cjs.map
|