llmist 0.5.0 → 0.6.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/{chunk-MH4TQ5AD.js → chunk-KB7LMYC2.js} +745 -287
- package/dist/chunk-KB7LMYC2.js.map +1 -0
- package/dist/{chunk-VF2WOCHM.js → chunk-YWJSGEYT.js} +4 -2
- package/dist/{chunk-VF2WOCHM.js.map → chunk-YWJSGEYT.js.map} +1 -1
- package/dist/cli.cjs +3168 -2687
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +95 -49
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +751 -304
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -3
- package/dist/index.d.ts +112 -3
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/{mock-stream-C8mBXRzJ.d.cts → mock-stream-CCe8vISa.d.cts} +45 -1
- package/dist/{mock-stream-C8mBXRzJ.d.ts → mock-stream-CCe8vISa.d.ts} +45 -1
- package/dist/testing/index.cjs +740 -287
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-LKIBXQ5I.js +0 -23
- package/dist/chunk-LKIBXQ5I.js.map +0 -1
- package/dist/chunk-MH4TQ5AD.js.map +0 -1
package/dist/testing/index.cjs
CHANGED
|
@@ -45,18 +45,28 @@ function parseLogLevel(value) {
|
|
|
45
45
|
}
|
|
46
46
|
return LEVEL_NAME_TO_ID[normalized];
|
|
47
47
|
}
|
|
48
|
+
function parseEnvBoolean(value) {
|
|
49
|
+
if (!value) return void 0;
|
|
50
|
+
const normalized = value.trim().toLowerCase();
|
|
51
|
+
if (normalized === "true" || normalized === "1") return true;
|
|
52
|
+
if (normalized === "false" || normalized === "0") return false;
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
48
55
|
function createLogger(options = {}) {
|
|
49
56
|
const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
|
|
50
57
|
const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
|
|
58
|
+
const envLogReset = parseEnvBoolean(process.env.LLMIST_LOG_RESET);
|
|
51
59
|
const minLevel = options.minLevel ?? envMinLevel ?? 4;
|
|
52
60
|
const defaultType = options.type ?? "pretty";
|
|
53
61
|
const name = options.name ?? "llmist";
|
|
62
|
+
const logReset = options.logReset ?? envLogReset ?? false;
|
|
54
63
|
let logFileStream;
|
|
55
64
|
let finalType = defaultType;
|
|
56
65
|
if (envLogFile) {
|
|
57
66
|
try {
|
|
58
67
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(envLogFile), { recursive: true });
|
|
59
|
-
|
|
68
|
+
const flags = logReset ? "w" : "a";
|
|
69
|
+
logFileStream = (0, import_node_fs.createWriteStream)(envLogFile, { flags });
|
|
60
70
|
finalType = "hidden";
|
|
61
71
|
} catch (error) {
|
|
62
72
|
console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
|
|
@@ -100,12 +110,16 @@ var init_logger = __esm({
|
|
|
100
110
|
});
|
|
101
111
|
|
|
102
112
|
// src/core/constants.ts
|
|
103
|
-
var GADGET_START_PREFIX, GADGET_END_PREFIX;
|
|
113
|
+
var GADGET_START_PREFIX, GADGET_END_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
|
|
104
114
|
var init_constants = __esm({
|
|
105
115
|
"src/core/constants.ts"() {
|
|
106
116
|
"use strict";
|
|
107
117
|
GADGET_START_PREFIX = "!!!GADGET_START:";
|
|
108
118
|
GADGET_END_PREFIX = "!!!GADGET_END";
|
|
119
|
+
DEFAULT_GADGET_OUTPUT_LIMIT = true;
|
|
120
|
+
DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
|
|
121
|
+
CHARS_PER_TOKEN = 4;
|
|
122
|
+
FALLBACK_CONTEXT_WINDOW = 128e3;
|
|
109
123
|
}
|
|
110
124
|
});
|
|
111
125
|
|
|
@@ -436,7 +450,7 @@ var init_prompt_config = __esm({
|
|
|
436
450
|
criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
|
|
437
451
|
formatDescriptionYaml: "Parameters in YAML format (one per line)",
|
|
438
452
|
formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
|
|
439
|
-
formatDescriptionToml: "Parameters in TOML format (key = value pairs, use
|
|
453
|
+
formatDescriptionToml: "Parameters in TOML format (key = value pairs, use heredoc for multiline: key = <<<EOF ... EOF)",
|
|
440
454
|
rules: () => [
|
|
441
455
|
"Output ONLY plain text with the exact markers - never use function/tool calling",
|
|
442
456
|
"You can invoke multiple gadgets in a single response",
|
|
@@ -617,10 +631,11 @@ ${this.endPrefix}
|
|
|
617
631
|
${this.startPrefix}analyze
|
|
618
632
|
type: economic_analysis
|
|
619
633
|
matter: "Polish Economy"
|
|
620
|
-
question:
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
634
|
+
question: <<<EOF
|
|
635
|
+
Analyze the following:
|
|
636
|
+
- Polish arms exports 2025
|
|
637
|
+
- Economic implications
|
|
638
|
+
EOF
|
|
624
639
|
${this.endPrefix}`,
|
|
625
640
|
json: `${this.startPrefix}translate
|
|
626
641
|
{"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
|
|
@@ -636,11 +651,11 @@ ${this.endPrefix}
|
|
|
636
651
|
${this.startPrefix}analyze
|
|
637
652
|
type = "economic_analysis"
|
|
638
653
|
matter = "Polish Economy"
|
|
639
|
-
question =
|
|
654
|
+
question = <<<EOF
|
|
640
655
|
Analyze the following:
|
|
641
656
|
- Polish arms exports 2025
|
|
642
657
|
- Economic implications
|
|
643
|
-
|
|
658
|
+
EOF
|
|
644
659
|
${this.endPrefix}`,
|
|
645
660
|
auto: `${this.startPrefix}translate
|
|
646
661
|
{"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
|
|
@@ -657,37 +672,38 @@ ${multipleExamples[parameterFormat]}`);
|
|
|
657
672
|
if (parameterFormat === "yaml") {
|
|
658
673
|
parts.push(`
|
|
659
674
|
|
|
660
|
-
YAML
|
|
661
|
-
For string values with
|
|
662
|
-
use the pipe (|) syntax. ALL content lines MUST be indented with 2 spaces:
|
|
675
|
+
YAML HEREDOC SYNTAX:
|
|
676
|
+
For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
|
|
663
677
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
- Option A: fast processing
|
|
668
|
-
- Option B: thorough analysis
|
|
669
|
-
Please choose one.
|
|
678
|
+
filePath: "README.md"
|
|
679
|
+
content: <<<EOF
|
|
680
|
+
# Project Title
|
|
670
681
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
682
|
+
This content can contain:
|
|
683
|
+
- Markdown lists
|
|
684
|
+
- Special characters: # : -
|
|
685
|
+
- Multiple paragraphs
|
|
686
|
+
EOF
|
|
687
|
+
|
|
688
|
+
The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.
|
|
689
|
+
No indentation is required for the content.`);
|
|
676
690
|
} else if (parameterFormat === "toml") {
|
|
677
691
|
parts.push(`
|
|
678
692
|
|
|
679
|
-
TOML
|
|
680
|
-
For string values with multiple lines
|
|
693
|
+
TOML HEREDOC SYNTAX:
|
|
694
|
+
For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
|
|
681
695
|
|
|
682
696
|
filePath = "README.md"
|
|
683
|
-
content =
|
|
697
|
+
content = <<<EOF
|
|
684
698
|
# Project Title
|
|
685
699
|
|
|
686
700
|
This content can contain:
|
|
687
701
|
- Markdown lists
|
|
688
702
|
- Special characters: # : -
|
|
689
703
|
- Multiple paragraphs
|
|
690
|
-
|
|
704
|
+
EOF
|
|
705
|
+
|
|
706
|
+
The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.`);
|
|
691
707
|
}
|
|
692
708
|
return parts.join("");
|
|
693
709
|
}
|
|
@@ -744,8 +760,534 @@ ${value}
|
|
|
744
760
|
}
|
|
745
761
|
return JSON.stringify(parameters);
|
|
746
762
|
}
|
|
747
|
-
build() {
|
|
748
|
-
return [...this.messages];
|
|
763
|
+
build() {
|
|
764
|
+
return [...this.messages];
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// src/gadgets/schema-to-json.ts
|
|
771
|
+
function schemaToJSONSchema(schema, options) {
|
|
772
|
+
const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
|
|
773
|
+
const mismatches = detectDescriptionMismatch(schema, jsonSchema);
|
|
774
|
+
if (mismatches.length > 0) {
|
|
775
|
+
defaultLogger.warn(
|
|
776
|
+
`Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
|
|
777
|
+
);
|
|
778
|
+
return mergeDescriptions(schema, jsonSchema);
|
|
779
|
+
}
|
|
780
|
+
return jsonSchema;
|
|
781
|
+
}
|
|
782
|
+
function detectDescriptionMismatch(schema, jsonSchema) {
|
|
783
|
+
const mismatches = [];
|
|
784
|
+
function checkSchema(zodSchema, json, path) {
|
|
785
|
+
if (!zodSchema || typeof zodSchema !== "object") return;
|
|
786
|
+
const def = zodSchema._def;
|
|
787
|
+
const jsonObj = json;
|
|
788
|
+
if (def?.description && !jsonObj?.description) {
|
|
789
|
+
mismatches.push(path || "root");
|
|
790
|
+
}
|
|
791
|
+
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
792
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
793
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
794
|
+
const properties = jsonObj?.properties;
|
|
795
|
+
const jsonProp = properties?.[key];
|
|
796
|
+
checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (def?.typeName === "ZodArray" && def?.type) {
|
|
800
|
+
checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
|
|
801
|
+
}
|
|
802
|
+
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
803
|
+
checkSchema(def.innerType, json, path);
|
|
804
|
+
}
|
|
805
|
+
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
806
|
+
checkSchema(def.innerType, json, path);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
checkSchema(schema, jsonSchema, "");
|
|
810
|
+
return mismatches;
|
|
811
|
+
}
|
|
812
|
+
function mergeDescriptions(schema, jsonSchema) {
|
|
813
|
+
function merge(zodSchema, json) {
|
|
814
|
+
if (!json || typeof json !== "object") return json;
|
|
815
|
+
const def = zodSchema._def;
|
|
816
|
+
const jsonObj = json;
|
|
817
|
+
const merged = { ...jsonObj };
|
|
818
|
+
if (def?.description && !jsonObj.description) {
|
|
819
|
+
merged.description = def.description;
|
|
820
|
+
}
|
|
821
|
+
if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
|
|
822
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
823
|
+
const properties = jsonObj.properties;
|
|
824
|
+
merged.properties = { ...properties };
|
|
825
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
826
|
+
if (properties[key]) {
|
|
827
|
+
merged.properties[key] = merge(fieldSchema, properties[key]);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
|
|
832
|
+
merged.items = merge(def.type, jsonObj.items);
|
|
833
|
+
}
|
|
834
|
+
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
835
|
+
return merge(def.innerType, json);
|
|
836
|
+
}
|
|
837
|
+
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
838
|
+
return merge(def.innerType, json);
|
|
839
|
+
}
|
|
840
|
+
return merged;
|
|
841
|
+
}
|
|
842
|
+
return merge(schema, jsonSchema);
|
|
843
|
+
}
|
|
844
|
+
var z2;
|
|
845
|
+
var init_schema_to_json = __esm({
|
|
846
|
+
"src/gadgets/schema-to-json.ts"() {
|
|
847
|
+
"use strict";
|
|
848
|
+
z2 = __toESM(require("zod"), 1);
|
|
849
|
+
init_logger();
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
// src/gadgets/gadget.ts
|
|
854
|
+
function findSafeDelimiter(content) {
|
|
855
|
+
const lines = content.split("\n");
|
|
856
|
+
for (const delimiter of HEREDOC_DELIMITERS) {
|
|
857
|
+
const regex = new RegExp(`^${delimiter}\\s*$`);
|
|
858
|
+
const isUsed = lines.some((line) => regex.test(line));
|
|
859
|
+
if (!isUsed) {
|
|
860
|
+
return delimiter;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
let counter = 1;
|
|
864
|
+
while (counter < 1e3) {
|
|
865
|
+
const delimiter = `HEREDOC_${counter}`;
|
|
866
|
+
const regex = new RegExp(`^${delimiter}\\s*$`);
|
|
867
|
+
const isUsed = lines.some((line) => regex.test(line));
|
|
868
|
+
if (!isUsed) {
|
|
869
|
+
return delimiter;
|
|
870
|
+
}
|
|
871
|
+
counter++;
|
|
872
|
+
}
|
|
873
|
+
return "HEREDOC_FALLBACK";
|
|
874
|
+
}
|
|
875
|
+
function formatYamlValue(value, indent = "") {
|
|
876
|
+
if (typeof value === "string") {
|
|
877
|
+
const lines = value.split("\n");
|
|
878
|
+
if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
|
|
879
|
+
return value;
|
|
880
|
+
}
|
|
881
|
+
const delimiter = findSafeDelimiter(value);
|
|
882
|
+
return `<<<${delimiter}
|
|
883
|
+
${value}
|
|
884
|
+
${delimiter}`;
|
|
885
|
+
}
|
|
886
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
887
|
+
return String(value);
|
|
888
|
+
}
|
|
889
|
+
if (value === null || value === void 0) {
|
|
890
|
+
return "null";
|
|
891
|
+
}
|
|
892
|
+
if (Array.isArray(value)) {
|
|
893
|
+
if (value.length === 0) return "[]";
|
|
894
|
+
const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
|
|
895
|
+
return "\n" + items.join("\n");
|
|
896
|
+
}
|
|
897
|
+
if (typeof value === "object") {
|
|
898
|
+
const entries = Object.entries(value);
|
|
899
|
+
if (entries.length === 0) return "{}";
|
|
900
|
+
const lines = entries.map(([k, v]) => {
|
|
901
|
+
const formattedValue = formatYamlValue(v, indent + " ");
|
|
902
|
+
if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
|
|
903
|
+
return `${indent}${k}: ${formattedValue}`;
|
|
904
|
+
}
|
|
905
|
+
return `${indent}${k}: ${formattedValue}`;
|
|
906
|
+
});
|
|
907
|
+
return "\n" + lines.join("\n");
|
|
908
|
+
}
|
|
909
|
+
return yaml.dump(value).trimEnd();
|
|
910
|
+
}
|
|
911
|
+
function formatParamsAsYaml(params) {
|
|
912
|
+
const lines = [];
|
|
913
|
+
for (const [key, value] of Object.entries(params)) {
|
|
914
|
+
const formattedValue = formatYamlValue(value, "");
|
|
915
|
+
if (formattedValue.startsWith("\n")) {
|
|
916
|
+
lines.push(`${key}:${formattedValue}`);
|
|
917
|
+
} else {
|
|
918
|
+
lines.push(`${key}: ${formattedValue}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return lines.join("\n");
|
|
922
|
+
}
|
|
923
|
+
function formatTomlValue(value) {
|
|
924
|
+
if (typeof value === "string") {
|
|
925
|
+
if (value.includes("\n")) {
|
|
926
|
+
const delimiter = findSafeDelimiter(value);
|
|
927
|
+
return `<<<${delimiter}
|
|
928
|
+
${value}
|
|
929
|
+
${delimiter}`;
|
|
930
|
+
}
|
|
931
|
+
return JSON.stringify(value);
|
|
932
|
+
}
|
|
933
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
934
|
+
return String(value);
|
|
935
|
+
}
|
|
936
|
+
if (value === null || value === void 0) {
|
|
937
|
+
return '""';
|
|
938
|
+
}
|
|
939
|
+
if (Array.isArray(value)) {
|
|
940
|
+
return JSON.stringify(value);
|
|
941
|
+
}
|
|
942
|
+
if (typeof value === "object") {
|
|
943
|
+
return JSON.stringify(value);
|
|
944
|
+
}
|
|
945
|
+
return JSON.stringify(value);
|
|
946
|
+
}
|
|
947
|
+
function formatParamsAsToml(params) {
|
|
948
|
+
const lines = [];
|
|
949
|
+
for (const [key, value] of Object.entries(params)) {
|
|
950
|
+
lines.push(`${key} = ${formatTomlValue(value)}`);
|
|
951
|
+
}
|
|
952
|
+
return lines.join("\n");
|
|
953
|
+
}
|
|
954
|
+
var yaml, HEREDOC_DELIMITERS, BaseGadget;
|
|
955
|
+
var init_gadget = __esm({
|
|
956
|
+
"src/gadgets/gadget.ts"() {
|
|
957
|
+
"use strict";
|
|
958
|
+
yaml = __toESM(require("js-yaml"), 1);
|
|
959
|
+
init_schema_to_json();
|
|
960
|
+
init_schema_validator();
|
|
961
|
+
HEREDOC_DELIMITERS = ["EOF", "END", "DOC", "CONTENT", "TEXT", "HEREDOC", "DATA", "BLOCK"];
|
|
962
|
+
BaseGadget = class {
|
|
963
|
+
/**
|
|
964
|
+
* The name of the gadget. Used for identification when LLM calls it.
|
|
965
|
+
* If not provided, defaults to the class name.
|
|
966
|
+
*/
|
|
967
|
+
name;
|
|
968
|
+
/**
|
|
969
|
+
* Optional Zod schema describing the expected input payload. When provided,
|
|
970
|
+
* it will be validated before execution and transformed into a JSON Schema
|
|
971
|
+
* representation that is surfaced to the LLM as part of the instructions.
|
|
972
|
+
*/
|
|
973
|
+
parameterSchema;
|
|
974
|
+
/**
|
|
975
|
+
* Optional timeout in milliseconds for gadget execution.
|
|
976
|
+
* If execution exceeds this timeout, a TimeoutException will be thrown.
|
|
977
|
+
* If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
|
|
978
|
+
* Set to 0 or undefined to disable timeout for this gadget.
|
|
979
|
+
*/
|
|
980
|
+
timeoutMs;
|
|
981
|
+
/**
|
|
982
|
+
* Optional usage examples to help LLMs understand proper invocation.
|
|
983
|
+
* Examples are rendered in getInstruction() alongside the schema.
|
|
984
|
+
*
|
|
985
|
+
* Note: Uses broader `unknown` type to allow typed examples from subclasses
|
|
986
|
+
* while maintaining runtime compatibility.
|
|
987
|
+
*/
|
|
988
|
+
examples;
|
|
989
|
+
/**
|
|
990
|
+
* Auto-generated instruction text for the LLM.
|
|
991
|
+
* Combines name, description, and parameter schema into a formatted instruction.
|
|
992
|
+
* @deprecated Use getInstruction(format) instead for format-specific schemas
|
|
993
|
+
*/
|
|
994
|
+
get instruction() {
|
|
995
|
+
return this.getInstruction("yaml");
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Generate instruction text for the LLM with format-specific schema.
|
|
999
|
+
* Combines name, description, and parameter schema into a formatted instruction.
|
|
1000
|
+
*
|
|
1001
|
+
* @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
|
|
1002
|
+
* @returns Formatted instruction string
|
|
1003
|
+
*/
|
|
1004
|
+
getInstruction(format = "json") {
|
|
1005
|
+
const parts = [];
|
|
1006
|
+
parts.push(this.description);
|
|
1007
|
+
if (this.parameterSchema) {
|
|
1008
|
+
const gadgetName = this.name ?? this.constructor.name;
|
|
1009
|
+
validateGadgetSchema(this.parameterSchema, gadgetName);
|
|
1010
|
+
const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
|
|
1011
|
+
target: "draft-7"
|
|
1012
|
+
});
|
|
1013
|
+
if (format === "json" || format === "auto") {
|
|
1014
|
+
parts.push("\n\nInput Schema (JSON):");
|
|
1015
|
+
parts.push(JSON.stringify(jsonSchema, null, 2));
|
|
1016
|
+
} else if (format === "toml") {
|
|
1017
|
+
parts.push("\n\nInput Schema (TOML):");
|
|
1018
|
+
parts.push(JSON.stringify(jsonSchema, null, 2));
|
|
1019
|
+
} else {
|
|
1020
|
+
const yamlSchema = yaml.dump(jsonSchema).trimEnd();
|
|
1021
|
+
parts.push("\n\nInput Schema (YAML):");
|
|
1022
|
+
parts.push(yamlSchema);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (this.examples && this.examples.length > 0) {
|
|
1026
|
+
parts.push("\n\nExamples:");
|
|
1027
|
+
this.examples.forEach((example, index) => {
|
|
1028
|
+
if (index > 0) {
|
|
1029
|
+
parts.push("");
|
|
1030
|
+
}
|
|
1031
|
+
if (example.comment) {
|
|
1032
|
+
parts.push(`# ${example.comment}`);
|
|
1033
|
+
}
|
|
1034
|
+
parts.push("Input:");
|
|
1035
|
+
if (format === "json" || format === "auto") {
|
|
1036
|
+
parts.push(JSON.stringify(example.params, null, 2));
|
|
1037
|
+
} else if (format === "toml") {
|
|
1038
|
+
parts.push(formatParamsAsToml(example.params));
|
|
1039
|
+
} else {
|
|
1040
|
+
parts.push(formatParamsAsYaml(example.params));
|
|
1041
|
+
}
|
|
1042
|
+
if (example.output !== void 0) {
|
|
1043
|
+
parts.push("Output:");
|
|
1044
|
+
parts.push(example.output);
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
return parts.join("\n");
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// src/gadgets/create-gadget.ts
|
|
1055
|
+
function createGadget(config) {
|
|
1056
|
+
class DynamicGadget extends BaseGadget {
|
|
1057
|
+
name = config.name;
|
|
1058
|
+
description = config.description;
|
|
1059
|
+
parameterSchema = config.schema;
|
|
1060
|
+
timeoutMs = config.timeoutMs;
|
|
1061
|
+
examples = config.examples;
|
|
1062
|
+
execute(params) {
|
|
1063
|
+
return config.execute(params);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return new DynamicGadget();
|
|
1067
|
+
}
|
|
1068
|
+
var init_create_gadget = __esm({
|
|
1069
|
+
"src/gadgets/create-gadget.ts"() {
|
|
1070
|
+
"use strict";
|
|
1071
|
+
init_gadget();
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
// src/gadgets/output-viewer.ts
|
|
1076
|
+
function applyPattern(lines, pattern) {
|
|
1077
|
+
const regex = new RegExp(pattern.regex);
|
|
1078
|
+
if (!pattern.include) {
|
|
1079
|
+
return lines.filter((line) => !regex.test(line));
|
|
1080
|
+
}
|
|
1081
|
+
const matchIndices = /* @__PURE__ */ new Set();
|
|
1082
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1083
|
+
if (regex.test(lines[i])) {
|
|
1084
|
+
const start = Math.max(0, i - pattern.before);
|
|
1085
|
+
const end = Math.min(lines.length - 1, i + pattern.after);
|
|
1086
|
+
for (let j = start; j <= end; j++) {
|
|
1087
|
+
matchIndices.add(j);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return lines.filter((_, index) => matchIndices.has(index));
|
|
1092
|
+
}
|
|
1093
|
+
function applyPatterns(lines, patterns) {
|
|
1094
|
+
let result = lines;
|
|
1095
|
+
for (const pattern of patterns) {
|
|
1096
|
+
result = applyPattern(result, pattern);
|
|
1097
|
+
}
|
|
1098
|
+
return result;
|
|
1099
|
+
}
|
|
1100
|
+
function applyLineLimit(lines, limit) {
|
|
1101
|
+
const trimmed = limit.trim();
|
|
1102
|
+
if (trimmed.endsWith("-") && !trimmed.startsWith("-")) {
|
|
1103
|
+
const n = parseInt(trimmed.slice(0, -1), 10);
|
|
1104
|
+
if (!isNaN(n) && n > 0) {
|
|
1105
|
+
return lines.slice(0, n);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (trimmed.startsWith("-") && !trimmed.includes("-", 1)) {
|
|
1109
|
+
const n = parseInt(trimmed, 10);
|
|
1110
|
+
if (!isNaN(n) && n < 0) {
|
|
1111
|
+
return lines.slice(n);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
|
|
1115
|
+
if (rangeMatch) {
|
|
1116
|
+
const start = parseInt(rangeMatch[1], 10);
|
|
1117
|
+
const end = parseInt(rangeMatch[2], 10);
|
|
1118
|
+
if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start) {
|
|
1119
|
+
return lines.slice(start - 1, end);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return lines;
|
|
1123
|
+
}
|
|
1124
|
+
function createGadgetOutputViewer(store) {
|
|
1125
|
+
return createGadget({
|
|
1126
|
+
name: "GadgetOutputViewer",
|
|
1127
|
+
description: "View stored output from gadgets that returned too much data. Use patterns to filter lines (like grep) and limit to control output size. Patterns are applied first in order, then the limit is applied to the result.",
|
|
1128
|
+
schema: import_zod.z.object({
|
|
1129
|
+
id: import_zod.z.string().describe("ID of the stored output (from the truncation message)"),
|
|
1130
|
+
patterns: import_zod.z.array(patternSchema).optional().describe(
|
|
1131
|
+
"Filter patterns applied in order (like piping through grep). Each pattern can include or exclude lines with optional before/after context."
|
|
1132
|
+
),
|
|
1133
|
+
limit: import_zod.z.string().optional().describe(
|
|
1134
|
+
"Line range to return after filtering. Formats: '100-' (first 100), '-25' (last 25), '50-100' (lines 50-100)"
|
|
1135
|
+
)
|
|
1136
|
+
}),
|
|
1137
|
+
examples: [
|
|
1138
|
+
{
|
|
1139
|
+
comment: "View first 50 lines of stored output",
|
|
1140
|
+
params: { id: "Search_abc12345", limit: "50-" }
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
comment: "Filter for error lines with context",
|
|
1144
|
+
params: {
|
|
1145
|
+
id: "Search_abc12345",
|
|
1146
|
+
patterns: [{ regex: "error|Error|ERROR", include: true, before: 2, after: 5 }]
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
comment: "Exclude blank lines, then show first 100",
|
|
1151
|
+
params: {
|
|
1152
|
+
id: "Search_abc12345",
|
|
1153
|
+
patterns: [{ regex: "^\\s*$", include: false, before: 0, after: 0 }],
|
|
1154
|
+
limit: "100-"
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
comment: "Chain filters: find TODOs, exclude tests, limit to 50 lines",
|
|
1159
|
+
params: {
|
|
1160
|
+
id: "Search_abc12345",
|
|
1161
|
+
patterns: [
|
|
1162
|
+
{ regex: "TODO", include: true, before: 1, after: 1 },
|
|
1163
|
+
{ regex: "test|spec", include: false, before: 0, after: 0 }
|
|
1164
|
+
],
|
|
1165
|
+
limit: "50-"
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
],
|
|
1169
|
+
execute: ({ id, patterns, limit }) => {
|
|
1170
|
+
const stored = store.get(id);
|
|
1171
|
+
if (!stored) {
|
|
1172
|
+
return `Error: No stored output with id "${id}". Available IDs: ${store.getIds().join(", ") || "(none)"}`;
|
|
1173
|
+
}
|
|
1174
|
+
let lines = stored.content.split("\n");
|
|
1175
|
+
if (patterns && patterns.length > 0) {
|
|
1176
|
+
lines = applyPatterns(
|
|
1177
|
+
lines,
|
|
1178
|
+
patterns.map((p) => ({
|
|
1179
|
+
regex: p.regex,
|
|
1180
|
+
include: p.include ?? true,
|
|
1181
|
+
before: p.before ?? 0,
|
|
1182
|
+
after: p.after ?? 0
|
|
1183
|
+
}))
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
if (limit) {
|
|
1187
|
+
lines = applyLineLimit(lines, limit);
|
|
1188
|
+
}
|
|
1189
|
+
const totalLines = stored.lineCount;
|
|
1190
|
+
const returnedLines = lines.length;
|
|
1191
|
+
if (returnedLines === 0) {
|
|
1192
|
+
return `No lines matched the filters. Original output had ${totalLines} lines.`;
|
|
1193
|
+
}
|
|
1194
|
+
const header = returnedLines < totalLines ? `[Showing ${returnedLines} of ${totalLines} lines]
|
|
1195
|
+
` : `[Showing all ${totalLines} lines]
|
|
1196
|
+
`;
|
|
1197
|
+
return header + lines.join("\n");
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
var import_zod, patternSchema;
|
|
1202
|
+
var init_output_viewer = __esm({
|
|
1203
|
+
"src/gadgets/output-viewer.ts"() {
|
|
1204
|
+
"use strict";
|
|
1205
|
+
import_zod = require("zod");
|
|
1206
|
+
init_create_gadget();
|
|
1207
|
+
patternSchema = import_zod.z.object({
|
|
1208
|
+
regex: import_zod.z.string().describe("Regular expression to match"),
|
|
1209
|
+
include: import_zod.z.boolean().default(true).describe("true = keep matching lines, false = exclude matching lines"),
|
|
1210
|
+
before: import_zod.z.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
|
|
1211
|
+
after: import_zod.z.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
// src/agent/gadget-output-store.ts
|
|
1217
|
+
var import_node_crypto, GadgetOutputStore;
|
|
1218
|
+
var init_gadget_output_store = __esm({
|
|
1219
|
+
"src/agent/gadget-output-store.ts"() {
|
|
1220
|
+
"use strict";
|
|
1221
|
+
import_node_crypto = require("crypto");
|
|
1222
|
+
GadgetOutputStore = class {
|
|
1223
|
+
outputs = /* @__PURE__ */ new Map();
|
|
1224
|
+
/**
|
|
1225
|
+
* Store a gadget output and return its ID.
|
|
1226
|
+
*
|
|
1227
|
+
* @param gadgetName - Name of the gadget that produced the output
|
|
1228
|
+
* @param content - Full output content to store
|
|
1229
|
+
* @returns Generated ID for retrieving the output later
|
|
1230
|
+
*/
|
|
1231
|
+
store(gadgetName, content) {
|
|
1232
|
+
const id = this.generateId(gadgetName);
|
|
1233
|
+
const encoder = new TextEncoder();
|
|
1234
|
+
const stored = {
|
|
1235
|
+
id,
|
|
1236
|
+
gadgetName,
|
|
1237
|
+
content,
|
|
1238
|
+
byteSize: encoder.encode(content).length,
|
|
1239
|
+
lineCount: content.split("\n").length,
|
|
1240
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1241
|
+
};
|
|
1242
|
+
this.outputs.set(id, stored);
|
|
1243
|
+
return id;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Retrieve a stored output by ID.
|
|
1247
|
+
*
|
|
1248
|
+
* @param id - The output ID (e.g., "Search_d34db33f")
|
|
1249
|
+
* @returns The stored output or undefined if not found
|
|
1250
|
+
*/
|
|
1251
|
+
get(id) {
|
|
1252
|
+
return this.outputs.get(id);
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Check if an output exists.
|
|
1256
|
+
*
|
|
1257
|
+
* @param id - The output ID to check
|
|
1258
|
+
* @returns True if the output exists
|
|
1259
|
+
*/
|
|
1260
|
+
has(id) {
|
|
1261
|
+
return this.outputs.has(id);
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Get all stored output IDs.
|
|
1265
|
+
*
|
|
1266
|
+
* @returns Array of output IDs
|
|
1267
|
+
*/
|
|
1268
|
+
getIds() {
|
|
1269
|
+
return Array.from(this.outputs.keys());
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Get the number of stored outputs.
|
|
1273
|
+
*/
|
|
1274
|
+
get size() {
|
|
1275
|
+
return this.outputs.size;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Clear all stored outputs.
|
|
1279
|
+
* Called when the agent run completes.
|
|
1280
|
+
*/
|
|
1281
|
+
clear() {
|
|
1282
|
+
this.outputs.clear();
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Generate a unique ID for a stored output.
|
|
1286
|
+
* Format: {GadgetName}_{8 hex chars}
|
|
1287
|
+
*/
|
|
1288
|
+
generateId(gadgetName) {
|
|
1289
|
+
const hex = (0, import_node_crypto.randomBytes)(4).toString("hex");
|
|
1290
|
+
return `${gadgetName}_${hex}`;
|
|
749
1291
|
}
|
|
750
1292
|
};
|
|
751
1293
|
}
|
|
@@ -1258,6 +1800,25 @@ function preprocessYaml(yamlStr) {
|
|
|
1258
1800
|
let i = 0;
|
|
1259
1801
|
while (i < lines.length) {
|
|
1260
1802
|
const line = lines[i];
|
|
1803
|
+
const heredocMatch = line.match(/^(\s*)([\w-]+):\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
1804
|
+
if (heredocMatch) {
|
|
1805
|
+
const [, indent, key, delimiter] = heredocMatch;
|
|
1806
|
+
const bodyLines = [];
|
|
1807
|
+
i++;
|
|
1808
|
+
const closingRegex = new RegExp(`^${delimiter}\\s*$`);
|
|
1809
|
+
while (i < lines.length && !closingRegex.test(lines[i])) {
|
|
1810
|
+
bodyLines.push(lines[i]);
|
|
1811
|
+
i++;
|
|
1812
|
+
}
|
|
1813
|
+
if (i < lines.length) {
|
|
1814
|
+
i++;
|
|
1815
|
+
}
|
|
1816
|
+
result.push(`${indent}${key}: |`);
|
|
1817
|
+
for (const bodyLine of bodyLines) {
|
|
1818
|
+
result.push(`${indent} ${bodyLine}`);
|
|
1819
|
+
}
|
|
1820
|
+
continue;
|
|
1821
|
+
}
|
|
1261
1822
|
const match = line.match(/^(\s*)([\w-]+):\s+(.+)$/);
|
|
1262
1823
|
if (match) {
|
|
1263
1824
|
const [, indent, key, value] = match;
|
|
@@ -1349,11 +1910,53 @@ function preprocessYaml(yamlStr) {
|
|
|
1349
1910
|
}
|
|
1350
1911
|
return result.join("\n");
|
|
1351
1912
|
}
|
|
1352
|
-
|
|
1913
|
+
function preprocessTomlHeredoc(tomlStr) {
|
|
1914
|
+
const lines = tomlStr.split("\n");
|
|
1915
|
+
const result = [];
|
|
1916
|
+
let i = 0;
|
|
1917
|
+
const heredocStartRegex = /^(\s*)([\w-]+)\s*=\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/;
|
|
1918
|
+
while (i < lines.length) {
|
|
1919
|
+
const line = lines[i];
|
|
1920
|
+
const match = line.match(heredocStartRegex);
|
|
1921
|
+
if (match) {
|
|
1922
|
+
const [, indent, key, delimiter] = match;
|
|
1923
|
+
const bodyLines = [];
|
|
1924
|
+
i++;
|
|
1925
|
+
const closingRegex = new RegExp(`^${delimiter}\\s*$`);
|
|
1926
|
+
let foundClosing = false;
|
|
1927
|
+
while (i < lines.length) {
|
|
1928
|
+
const bodyLine = lines[i];
|
|
1929
|
+
if (closingRegex.test(bodyLine)) {
|
|
1930
|
+
foundClosing = true;
|
|
1931
|
+
i++;
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
bodyLines.push(bodyLine);
|
|
1935
|
+
i++;
|
|
1936
|
+
}
|
|
1937
|
+
if (bodyLines.length === 0) {
|
|
1938
|
+
result.push(`${indent}${key} = """"""`);
|
|
1939
|
+
} else {
|
|
1940
|
+
result.push(`${indent}${key} = """`);
|
|
1941
|
+
for (let j = 0; j < bodyLines.length - 1; j++) {
|
|
1942
|
+
result.push(bodyLines[j]);
|
|
1943
|
+
}
|
|
1944
|
+
result.push(`${bodyLines[bodyLines.length - 1]}"""`);
|
|
1945
|
+
}
|
|
1946
|
+
if (!foundClosing) {
|
|
1947
|
+
}
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
result.push(line);
|
|
1951
|
+
i++;
|
|
1952
|
+
}
|
|
1953
|
+
return result.join("\n");
|
|
1954
|
+
}
|
|
1955
|
+
var yaml2, import_js_toml, globalInvocationCounter, StreamParser;
|
|
1353
1956
|
var init_parser = __esm({
|
|
1354
1957
|
"src/gadgets/parser.ts"() {
|
|
1355
1958
|
"use strict";
|
|
1356
|
-
|
|
1959
|
+
yaml2 = __toESM(require("js-yaml"), 1);
|
|
1357
1960
|
import_js_toml = require("js-toml");
|
|
1358
1961
|
init_constants();
|
|
1359
1962
|
globalInvocationCounter = 0;
|
|
@@ -1400,14 +2003,14 @@ var init_parser = __esm({
|
|
|
1400
2003
|
}
|
|
1401
2004
|
if (this.parameterFormat === "yaml") {
|
|
1402
2005
|
try {
|
|
1403
|
-
return { parameters:
|
|
2006
|
+
return { parameters: yaml2.load(preprocessYaml(raw)) };
|
|
1404
2007
|
} catch (error) {
|
|
1405
2008
|
return { parseError: error instanceof Error ? error.message : "Failed to parse YAML" };
|
|
1406
2009
|
}
|
|
1407
2010
|
}
|
|
1408
2011
|
if (this.parameterFormat === "toml") {
|
|
1409
2012
|
try {
|
|
1410
|
-
return { parameters: (0, import_js_toml.load)(raw) };
|
|
2013
|
+
return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
|
|
1411
2014
|
} catch (error) {
|
|
1412
2015
|
return { parseError: error instanceof Error ? error.message : "Failed to parse TOML" };
|
|
1413
2016
|
}
|
|
@@ -1416,10 +2019,10 @@ var init_parser = __esm({
|
|
|
1416
2019
|
return { parameters: JSON.parse(raw) };
|
|
1417
2020
|
} catch {
|
|
1418
2021
|
try {
|
|
1419
|
-
return { parameters: (0, import_js_toml.load)(raw) };
|
|
2022
|
+
return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
|
|
1420
2023
|
} catch {
|
|
1421
2024
|
try {
|
|
1422
|
-
return { parameters:
|
|
2025
|
+
return { parameters: yaml2.load(preprocessYaml(raw)) };
|
|
1423
2026
|
} catch (error) {
|
|
1424
2027
|
return {
|
|
1425
2028
|
parseError: error instanceof Error ? error.message : "Failed to parse as JSON, TOML, or YAML"
|
|
@@ -1941,9 +2544,12 @@ var Agent;
|
|
|
1941
2544
|
var init_agent = __esm({
|
|
1942
2545
|
"src/agent/agent.ts"() {
|
|
1943
2546
|
"use strict";
|
|
2547
|
+
init_constants();
|
|
1944
2548
|
init_messages();
|
|
1945
2549
|
init_model_shortcuts();
|
|
2550
|
+
init_output_viewer();
|
|
1946
2551
|
init_logger();
|
|
2552
|
+
init_gadget_output_store();
|
|
1947
2553
|
init_agent_internal_key();
|
|
1948
2554
|
init_conversation_manager();
|
|
1949
2555
|
init_event_handlers();
|
|
@@ -1968,6 +2574,10 @@ var init_agent = __esm({
|
|
|
1968
2574
|
defaultGadgetTimeoutMs;
|
|
1969
2575
|
defaultMaxTokens;
|
|
1970
2576
|
userPromptProvided;
|
|
2577
|
+
// Gadget output limiting
|
|
2578
|
+
outputStore;
|
|
2579
|
+
outputLimitEnabled;
|
|
2580
|
+
outputLimitCharLimit;
|
|
1971
2581
|
/**
|
|
1972
2582
|
* Creates a new Agent instance.
|
|
1973
2583
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -1983,7 +2593,6 @@ var init_agent = __esm({
|
|
|
1983
2593
|
this.maxIterations = options.maxIterations ?? 10;
|
|
1984
2594
|
this.temperature = options.temperature;
|
|
1985
2595
|
this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
|
|
1986
|
-
this.hooks = options.hooks ?? {};
|
|
1987
2596
|
this.registry = options.registry;
|
|
1988
2597
|
this.parameterFormat = options.parameterFormat ?? "json";
|
|
1989
2598
|
this.gadgetStartPrefix = options.gadgetStartPrefix;
|
|
@@ -1994,6 +2603,16 @@ var init_agent = __esm({
|
|
|
1994
2603
|
this.shouldContinueAfterError = options.shouldContinueAfterError;
|
|
1995
2604
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
1996
2605
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
2606
|
+
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
2607
|
+
this.outputStore = new GadgetOutputStore();
|
|
2608
|
+
const limitPercent = options.gadgetOutputLimitPercent ?? DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT;
|
|
2609
|
+
const limits = this.client.modelRegistry.getModelLimits(this.model);
|
|
2610
|
+
const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
|
|
2611
|
+
this.outputLimitCharLimit = Math.floor(contextWindow * (limitPercent / 100) * CHARS_PER_TOKEN);
|
|
2612
|
+
if (this.outputLimitEnabled) {
|
|
2613
|
+
this.registry.register("GadgetOutputViewer", createGadgetOutputViewer(this.outputStore));
|
|
2614
|
+
}
|
|
2615
|
+
this.hooks = this.mergeOutputLimiterHook(options.hooks);
|
|
1997
2616
|
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
1998
2617
|
if (options.systemPrompt) {
|
|
1999
2618
|
baseBuilder.addSystem(options.systemPrompt);
|
|
@@ -2298,6 +2917,43 @@ var init_agent = __esm({
|
|
|
2298
2917
|
}
|
|
2299
2918
|
return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
|
|
2300
2919
|
}
|
|
2920
|
+
/**
|
|
2921
|
+
* Merge the output limiter interceptor into user-provided hooks.
|
|
2922
|
+
* The limiter runs first, then chains to any user interceptor.
|
|
2923
|
+
*/
|
|
2924
|
+
mergeOutputLimiterHook(userHooks) {
|
|
2925
|
+
if (!this.outputLimitEnabled) {
|
|
2926
|
+
return userHooks ?? {};
|
|
2927
|
+
}
|
|
2928
|
+
const limiterInterceptor = (result, ctx) => {
|
|
2929
|
+
if (ctx.gadgetName === "GadgetOutputViewer") {
|
|
2930
|
+
return result;
|
|
2931
|
+
}
|
|
2932
|
+
if (result.length > this.outputLimitCharLimit) {
|
|
2933
|
+
const id = this.outputStore.store(ctx.gadgetName, result);
|
|
2934
|
+
const lines = result.split("\n").length;
|
|
2935
|
+
const bytes = new TextEncoder().encode(result).length;
|
|
2936
|
+
this.logger.info("Gadget output exceeded limit, stored for browsing", {
|
|
2937
|
+
gadgetName: ctx.gadgetName,
|
|
2938
|
+
outputId: id,
|
|
2939
|
+
bytes,
|
|
2940
|
+
lines,
|
|
2941
|
+
charLimit: this.outputLimitCharLimit
|
|
2942
|
+
});
|
|
2943
|
+
return `[Gadget "${ctx.gadgetName}" returned too much data: ${bytes.toLocaleString()} bytes, ${lines.toLocaleString()} lines. Use GadgetOutputViewer with id "${id}" to read it]`;
|
|
2944
|
+
}
|
|
2945
|
+
return result;
|
|
2946
|
+
};
|
|
2947
|
+
const userInterceptor = userHooks?.interceptors?.interceptGadgetResult;
|
|
2948
|
+
const chainedInterceptor = userInterceptor ? (result, ctx) => userInterceptor(limiterInterceptor(result, ctx), ctx) : limiterInterceptor;
|
|
2949
|
+
return {
|
|
2950
|
+
...userHooks,
|
|
2951
|
+
interceptors: {
|
|
2952
|
+
...userHooks?.interceptors,
|
|
2953
|
+
interceptGadgetResult: chainedInterceptor
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2301
2957
|
/**
|
|
2302
2958
|
* Run agent with named event handlers (syntactic sugar).
|
|
2303
2959
|
*
|
|
@@ -2350,6 +3006,8 @@ var init_builder = __esm({
|
|
|
2350
3006
|
stopOnGadgetError;
|
|
2351
3007
|
shouldContinueAfterError;
|
|
2352
3008
|
defaultGadgetTimeoutMs;
|
|
3009
|
+
gadgetOutputLimit;
|
|
3010
|
+
gadgetOutputLimitPercent;
|
|
2353
3011
|
constructor(client) {
|
|
2354
3012
|
this.client = client;
|
|
2355
3013
|
}
|
|
@@ -2682,6 +3340,45 @@ var init_builder = __esm({
|
|
|
2682
3340
|
this.defaultGadgetTimeoutMs = timeoutMs;
|
|
2683
3341
|
return this;
|
|
2684
3342
|
}
|
|
3343
|
+
/**
|
|
3344
|
+
* Enable or disable gadget output limiting.
|
|
3345
|
+
*
|
|
3346
|
+
* When enabled, gadget outputs exceeding the configured limit are stored
|
|
3347
|
+
* and can be browsed using the GadgetOutputViewer gadget.
|
|
3348
|
+
*
|
|
3349
|
+
* @param enabled - Whether to enable output limiting (default: true)
|
|
3350
|
+
* @returns This builder for chaining
|
|
3351
|
+
*
|
|
3352
|
+
* @example
|
|
3353
|
+
* ```typescript
|
|
3354
|
+
* .withGadgetOutputLimit(false) // Disable output limiting
|
|
3355
|
+
* ```
|
|
3356
|
+
*/
|
|
3357
|
+
withGadgetOutputLimit(enabled) {
|
|
3358
|
+
this.gadgetOutputLimit = enabled;
|
|
3359
|
+
return this;
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Set the maximum gadget output as a percentage of the model's context window.
|
|
3363
|
+
*
|
|
3364
|
+
* Outputs exceeding this limit are stored for later browsing with GadgetOutputViewer.
|
|
3365
|
+
*
|
|
3366
|
+
* @param percent - Percentage of context window (1-100, default: 15)
|
|
3367
|
+
* @returns This builder for chaining
|
|
3368
|
+
* @throws {Error} If percent is not between 1 and 100
|
|
3369
|
+
*
|
|
3370
|
+
* @example
|
|
3371
|
+
* ```typescript
|
|
3372
|
+
* .withGadgetOutputLimitPercent(25) // 25% of context window
|
|
3373
|
+
* ```
|
|
3374
|
+
*/
|
|
3375
|
+
withGadgetOutputLimitPercent(percent) {
|
|
3376
|
+
if (percent < 1 || percent > 100) {
|
|
3377
|
+
throw new Error("Output limit percent must be between 1 and 100");
|
|
3378
|
+
}
|
|
3379
|
+
this.gadgetOutputLimitPercent = percent;
|
|
3380
|
+
return this;
|
|
3381
|
+
}
|
|
2685
3382
|
/**
|
|
2686
3383
|
* Build and create the agent with the given user prompt.
|
|
2687
3384
|
* Returns the Agent instance ready to run.
|
|
@@ -2726,7 +3423,9 @@ var init_builder = __esm({
|
|
|
2726
3423
|
textOnlyHandler: this.textOnlyHandler,
|
|
2727
3424
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
2728
3425
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
2729
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
|
|
3426
|
+
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3427
|
+
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
3428
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
2730
3429
|
};
|
|
2731
3430
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
2732
3431
|
}
|
|
@@ -2825,7 +3524,9 @@ var init_builder = __esm({
|
|
|
2825
3524
|
textOnlyHandler: this.textOnlyHandler,
|
|
2826
3525
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
2827
3526
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
2828
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
|
|
3527
|
+
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3528
|
+
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
3529
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
2829
3530
|
};
|
|
2830
3531
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
2831
3532
|
}
|
|
@@ -5266,256 +5967,8 @@ function createMockClient(options) {
|
|
|
5266
5967
|
});
|
|
5267
5968
|
}
|
|
5268
5969
|
|
|
5269
|
-
// src/gadgets/gadget.ts
|
|
5270
|
-
var yaml2 = __toESM(require("js-yaml"), 1);
|
|
5271
|
-
|
|
5272
|
-
// src/gadgets/schema-to-json.ts
|
|
5273
|
-
var z2 = __toESM(require("zod"), 1);
|
|
5274
|
-
init_logger();
|
|
5275
|
-
function schemaToJSONSchema(schema, options) {
|
|
5276
|
-
const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
|
|
5277
|
-
const mismatches = detectDescriptionMismatch(schema, jsonSchema);
|
|
5278
|
-
if (mismatches.length > 0) {
|
|
5279
|
-
defaultLogger.warn(
|
|
5280
|
-
`Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
|
|
5281
|
-
);
|
|
5282
|
-
return mergeDescriptions(schema, jsonSchema);
|
|
5283
|
-
}
|
|
5284
|
-
return jsonSchema;
|
|
5285
|
-
}
|
|
5286
|
-
function detectDescriptionMismatch(schema, jsonSchema) {
|
|
5287
|
-
const mismatches = [];
|
|
5288
|
-
function checkSchema(zodSchema, json, path) {
|
|
5289
|
-
if (!zodSchema || typeof zodSchema !== "object") return;
|
|
5290
|
-
const def = zodSchema._def;
|
|
5291
|
-
const jsonObj = json;
|
|
5292
|
-
if (def?.description && !jsonObj?.description) {
|
|
5293
|
-
mismatches.push(path || "root");
|
|
5294
|
-
}
|
|
5295
|
-
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
5296
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
5297
|
-
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
5298
|
-
const properties = jsonObj?.properties;
|
|
5299
|
-
const jsonProp = properties?.[key];
|
|
5300
|
-
checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
|
|
5301
|
-
}
|
|
5302
|
-
}
|
|
5303
|
-
if (def?.typeName === "ZodArray" && def?.type) {
|
|
5304
|
-
checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
|
|
5305
|
-
}
|
|
5306
|
-
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
5307
|
-
checkSchema(def.innerType, json, path);
|
|
5308
|
-
}
|
|
5309
|
-
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
5310
|
-
checkSchema(def.innerType, json, path);
|
|
5311
|
-
}
|
|
5312
|
-
}
|
|
5313
|
-
checkSchema(schema, jsonSchema, "");
|
|
5314
|
-
return mismatches;
|
|
5315
|
-
}
|
|
5316
|
-
function mergeDescriptions(schema, jsonSchema) {
|
|
5317
|
-
function merge(zodSchema, json) {
|
|
5318
|
-
if (!json || typeof json !== "object") return json;
|
|
5319
|
-
const def = zodSchema._def;
|
|
5320
|
-
const jsonObj = json;
|
|
5321
|
-
const merged = { ...jsonObj };
|
|
5322
|
-
if (def?.description && !jsonObj.description) {
|
|
5323
|
-
merged.description = def.description;
|
|
5324
|
-
}
|
|
5325
|
-
if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
|
|
5326
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
5327
|
-
const properties = jsonObj.properties;
|
|
5328
|
-
merged.properties = { ...properties };
|
|
5329
|
-
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
5330
|
-
if (properties[key]) {
|
|
5331
|
-
merged.properties[key] = merge(fieldSchema, properties[key]);
|
|
5332
|
-
}
|
|
5333
|
-
}
|
|
5334
|
-
}
|
|
5335
|
-
if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
|
|
5336
|
-
merged.items = merge(def.type, jsonObj.items);
|
|
5337
|
-
}
|
|
5338
|
-
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
5339
|
-
return merge(def.innerType, json);
|
|
5340
|
-
}
|
|
5341
|
-
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
5342
|
-
return merge(def.innerType, json);
|
|
5343
|
-
}
|
|
5344
|
-
return merged;
|
|
5345
|
-
}
|
|
5346
|
-
return merge(schema, jsonSchema);
|
|
5347
|
-
}
|
|
5348
|
-
|
|
5349
|
-
// src/gadgets/gadget.ts
|
|
5350
|
-
init_schema_validator();
|
|
5351
|
-
function formatYamlValue(value, indent = "") {
|
|
5352
|
-
if (typeof value === "string") {
|
|
5353
|
-
const lines = value.split("\n");
|
|
5354
|
-
if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
|
|
5355
|
-
return value;
|
|
5356
|
-
}
|
|
5357
|
-
const indentedLines = lines.map((line) => `${indent} ${line}`).join("\n");
|
|
5358
|
-
return `|
|
|
5359
|
-
${indentedLines}`;
|
|
5360
|
-
}
|
|
5361
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
5362
|
-
return String(value);
|
|
5363
|
-
}
|
|
5364
|
-
if (value === null || value === void 0) {
|
|
5365
|
-
return "null";
|
|
5366
|
-
}
|
|
5367
|
-
if (Array.isArray(value)) {
|
|
5368
|
-
if (value.length === 0) return "[]";
|
|
5369
|
-
const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
|
|
5370
|
-
return "\n" + items.join("\n");
|
|
5371
|
-
}
|
|
5372
|
-
if (typeof value === "object") {
|
|
5373
|
-
const entries = Object.entries(value);
|
|
5374
|
-
if (entries.length === 0) return "{}";
|
|
5375
|
-
const lines = entries.map(([k, v]) => {
|
|
5376
|
-
const formattedValue = formatYamlValue(v, indent + " ");
|
|
5377
|
-
if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
|
|
5378
|
-
return `${indent}${k}: ${formattedValue}`;
|
|
5379
|
-
}
|
|
5380
|
-
return `${indent}${k}: ${formattedValue}`;
|
|
5381
|
-
});
|
|
5382
|
-
return "\n" + lines.join("\n");
|
|
5383
|
-
}
|
|
5384
|
-
return yaml2.dump(value).trimEnd();
|
|
5385
|
-
}
|
|
5386
|
-
function formatParamsAsYaml(params) {
|
|
5387
|
-
const lines = [];
|
|
5388
|
-
for (const [key, value] of Object.entries(params)) {
|
|
5389
|
-
const formattedValue = formatYamlValue(value, "");
|
|
5390
|
-
if (formattedValue.startsWith("\n")) {
|
|
5391
|
-
lines.push(`${key}:${formattedValue}`);
|
|
5392
|
-
} else {
|
|
5393
|
-
lines.push(`${key}: ${formattedValue}`);
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
return lines.join("\n");
|
|
5397
|
-
}
|
|
5398
|
-
function formatTomlValue(value) {
|
|
5399
|
-
if (typeof value === "string") {
|
|
5400
|
-
if (value.includes("\n")) {
|
|
5401
|
-
return `"""
|
|
5402
|
-
${value}
|
|
5403
|
-
"""`;
|
|
5404
|
-
}
|
|
5405
|
-
return JSON.stringify(value);
|
|
5406
|
-
}
|
|
5407
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
5408
|
-
return String(value);
|
|
5409
|
-
}
|
|
5410
|
-
if (value === null || value === void 0) {
|
|
5411
|
-
return '""';
|
|
5412
|
-
}
|
|
5413
|
-
if (Array.isArray(value)) {
|
|
5414
|
-
return JSON.stringify(value);
|
|
5415
|
-
}
|
|
5416
|
-
if (typeof value === "object") {
|
|
5417
|
-
return JSON.stringify(value);
|
|
5418
|
-
}
|
|
5419
|
-
return JSON.stringify(value);
|
|
5420
|
-
}
|
|
5421
|
-
function formatParamsAsToml(params) {
|
|
5422
|
-
const lines = [];
|
|
5423
|
-
for (const [key, value] of Object.entries(params)) {
|
|
5424
|
-
lines.push(`${key} = ${formatTomlValue(value)}`);
|
|
5425
|
-
}
|
|
5426
|
-
return lines.join("\n");
|
|
5427
|
-
}
|
|
5428
|
-
var BaseGadget = class {
|
|
5429
|
-
/**
|
|
5430
|
-
* The name of the gadget. Used for identification when LLM calls it.
|
|
5431
|
-
* If not provided, defaults to the class name.
|
|
5432
|
-
*/
|
|
5433
|
-
name;
|
|
5434
|
-
/**
|
|
5435
|
-
* Optional Zod schema describing the expected input payload. When provided,
|
|
5436
|
-
* it will be validated before execution and transformed into a JSON Schema
|
|
5437
|
-
* representation that is surfaced to the LLM as part of the instructions.
|
|
5438
|
-
*/
|
|
5439
|
-
parameterSchema;
|
|
5440
|
-
/**
|
|
5441
|
-
* Optional timeout in milliseconds for gadget execution.
|
|
5442
|
-
* If execution exceeds this timeout, a TimeoutException will be thrown.
|
|
5443
|
-
* If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
|
|
5444
|
-
* Set to 0 or undefined to disable timeout for this gadget.
|
|
5445
|
-
*/
|
|
5446
|
-
timeoutMs;
|
|
5447
|
-
/**
|
|
5448
|
-
* Optional usage examples to help LLMs understand proper invocation.
|
|
5449
|
-
* Examples are rendered in getInstruction() alongside the schema.
|
|
5450
|
-
*
|
|
5451
|
-
* Note: Uses broader `unknown` type to allow typed examples from subclasses
|
|
5452
|
-
* while maintaining runtime compatibility.
|
|
5453
|
-
*/
|
|
5454
|
-
examples;
|
|
5455
|
-
/**
|
|
5456
|
-
* Auto-generated instruction text for the LLM.
|
|
5457
|
-
* Combines name, description, and parameter schema into a formatted instruction.
|
|
5458
|
-
* @deprecated Use getInstruction(format) instead for format-specific schemas
|
|
5459
|
-
*/
|
|
5460
|
-
get instruction() {
|
|
5461
|
-
return this.getInstruction("yaml");
|
|
5462
|
-
}
|
|
5463
|
-
/**
|
|
5464
|
-
* Generate instruction text for the LLM with format-specific schema.
|
|
5465
|
-
* Combines name, description, and parameter schema into a formatted instruction.
|
|
5466
|
-
*
|
|
5467
|
-
* @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
|
|
5468
|
-
* @returns Formatted instruction string
|
|
5469
|
-
*/
|
|
5470
|
-
getInstruction(format = "json") {
|
|
5471
|
-
const parts = [];
|
|
5472
|
-
parts.push(this.description);
|
|
5473
|
-
if (this.parameterSchema) {
|
|
5474
|
-
const gadgetName = this.name ?? this.constructor.name;
|
|
5475
|
-
validateGadgetSchema(this.parameterSchema, gadgetName);
|
|
5476
|
-
const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
|
|
5477
|
-
target: "draft-7"
|
|
5478
|
-
});
|
|
5479
|
-
if (format === "json" || format === "auto") {
|
|
5480
|
-
parts.push("\n\nInput Schema (JSON):");
|
|
5481
|
-
parts.push(JSON.stringify(jsonSchema, null, 2));
|
|
5482
|
-
} else if (format === "toml") {
|
|
5483
|
-
parts.push("\n\nInput Schema (TOML):");
|
|
5484
|
-
parts.push(JSON.stringify(jsonSchema, null, 2));
|
|
5485
|
-
} else {
|
|
5486
|
-
const yamlSchema = yaml2.dump(jsonSchema).trimEnd();
|
|
5487
|
-
parts.push("\n\nInput Schema (YAML):");
|
|
5488
|
-
parts.push(yamlSchema);
|
|
5489
|
-
}
|
|
5490
|
-
}
|
|
5491
|
-
if (this.examples && this.examples.length > 0) {
|
|
5492
|
-
parts.push("\n\nExamples:");
|
|
5493
|
-
this.examples.forEach((example, index) => {
|
|
5494
|
-
if (index > 0) {
|
|
5495
|
-
parts.push("");
|
|
5496
|
-
}
|
|
5497
|
-
if (example.comment) {
|
|
5498
|
-
parts.push(`# ${example.comment}`);
|
|
5499
|
-
}
|
|
5500
|
-
parts.push("Input:");
|
|
5501
|
-
if (format === "json" || format === "auto") {
|
|
5502
|
-
parts.push(JSON.stringify(example.params, null, 2));
|
|
5503
|
-
} else if (format === "toml") {
|
|
5504
|
-
parts.push(formatParamsAsToml(example.params));
|
|
5505
|
-
} else {
|
|
5506
|
-
parts.push(formatParamsAsYaml(example.params));
|
|
5507
|
-
}
|
|
5508
|
-
if (example.output !== void 0) {
|
|
5509
|
-
parts.push("Output:");
|
|
5510
|
-
parts.push(example.output);
|
|
5511
|
-
}
|
|
5512
|
-
});
|
|
5513
|
-
}
|
|
5514
|
-
return parts.join("\n");
|
|
5515
|
-
}
|
|
5516
|
-
};
|
|
5517
|
-
|
|
5518
5970
|
// src/testing/mock-gadget.ts
|
|
5971
|
+
init_gadget();
|
|
5519
5972
|
var MockGadgetImpl = class extends BaseGadget {
|
|
5520
5973
|
name;
|
|
5521
5974
|
description;
|