@stackfactor/agent-utils 1.2.11 → 1.2.14
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/README.md +4 -0
- package/dist/cjs/langChain.d.ts.map +1 -1
- package/dist/cjs/langChain.js +200 -22
- package/dist/esm/langChain.d.ts.map +1 -1
- package/dist/esm/langChain.js +200 -22
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -300,3 +300,7 @@ The `config` object accepted by LangChain methods supports the following keys:
|
|
|
300
300
|
## License
|
|
301
301
|
|
|
302
302
|
Not licensed — proprietary software of StackFactor Inc.
|
|
303
|
+
|
|
304
|
+
## Development and release
|
|
305
|
+
|
|
306
|
+
See [docs/README.md](docs/README.md) — branch flow, how to cut a release, and troubleshooting.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAuGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;wBAktB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBA0BG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCA4XF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA//B8B,GAAG,KAAG,MAAM;+CAprB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAosBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAsiCT,wBASE"}
|
package/dist/cjs/langChain.js
CHANGED
|
@@ -12,7 +12,8 @@ const const_js_1 = __importDefault(require("./const.js"));
|
|
|
12
12
|
const langchain_1 = require("langchain");
|
|
13
13
|
const errorHandling_js_1 = __importDefault(require("./errorHandling.js"));
|
|
14
14
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
15
|
-
const
|
|
15
|
+
const zod_1 = require("zod");
|
|
16
|
+
const transform_json_schema_1 = require("@anthropic-ai/sdk/lib/transform-json-schema");
|
|
16
17
|
const runtimeContext_js_1 = require("./runtimeContext.js");
|
|
17
18
|
const JSON_ESCAPE_INSTRUCTION = `
|
|
18
19
|
CRITICAL - Your response must be valid JSON. Escape ALL special characters in string values:
|
|
@@ -23,6 +24,74 @@ CRITICAL - Your response must be valid JSON. Escape ALL special characters in st
|
|
|
23
24
|
- Backslashes → \\\\
|
|
24
25
|
Do NOT include raw newlines, tabs, or unescaped quotes inside JSON string values.
|
|
25
26
|
`.trim();
|
|
27
|
+
/**
|
|
28
|
+
* Providers for which `getLLMModel` configures native, schema-constrained JSON
|
|
29
|
+
* output (OpenAI `response_format`, Anthropic `output_config.format`, Gemini
|
|
30
|
+
* `responseSchema` + JSON mode). For these the model emits JSON directly, so the
|
|
31
|
+
* prompt-injected schema and the schema-validation retry are skipped. Other
|
|
32
|
+
* providers (DeepSeek/Kimi/GLM via the OpenAI-compatible shim) have no native
|
|
33
|
+
* support here and continue to rely on prompt instructions + validation.
|
|
34
|
+
* @param modelName - The model identifier being routed
|
|
35
|
+
* @returns `true` when native structured output is wired up for the provider
|
|
36
|
+
*/
|
|
37
|
+
const supportsNativeSchema = (modelName) => modelName.startsWith("gpt-") ||
|
|
38
|
+
modelName.startsWith("claude-") ||
|
|
39
|
+
modelName.startsWith("gemini-");
|
|
40
|
+
/**
|
|
41
|
+
* Converts a Zod v4 schema to a JSON Schema using zod's built-in
|
|
42
|
+
* `z.toJSONSchema`. The classic `zod-to-json-schema` package only understands
|
|
43
|
+
* zod v3 and silently returns `{}` for v4 schemas, which would send empty
|
|
44
|
+
* schemas to every provider. Targets OpenAPI 3.0 — the dialect omits the
|
|
45
|
+
* `$schema` keyword and is the widest-compatible across providers — and inlines
|
|
46
|
+
* reused subschemas so the result carries no `$ref`/`$defs` (which Gemini's
|
|
47
|
+
* `responseSchema` rejects).
|
|
48
|
+
* @param schema - A Zod schema
|
|
49
|
+
* @returns The equivalent JSON Schema object
|
|
50
|
+
*/
|
|
51
|
+
const buildJsonSchema = (schema) => zod_1.z.toJSONSchema(schema, { target: "openapi-3.0", reused: "inline" });
|
|
52
|
+
/**
|
|
53
|
+
* Recursively enforces OpenAI strict structured-output rules on a JSON Schema:
|
|
54
|
+
* every object gets `additionalProperties: false` and lists all of its
|
|
55
|
+
* properties in `required`. OpenAI's `strict: true` mode rejects optional
|
|
56
|
+
* properties, so all fields are marked required (matching OpenAI's own schema
|
|
57
|
+
* transform). Mutates and returns the schema.
|
|
58
|
+
* @param s - A JSON Schema object (mutated in place)
|
|
59
|
+
* @returns The same object, made strict-compatible
|
|
60
|
+
*/
|
|
61
|
+
const strictifyJsonSchema = (s) => {
|
|
62
|
+
if (!s || typeof s !== "object")
|
|
63
|
+
return s;
|
|
64
|
+
if (s.type === "object" && s.properties) {
|
|
65
|
+
s.additionalProperties = false;
|
|
66
|
+
s.required = Object.keys(s.properties);
|
|
67
|
+
for (const key of Object.keys(s.properties)) {
|
|
68
|
+
strictifyJsonSchema(s.properties[key]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (s.type === "array" && s.items)
|
|
72
|
+
strictifyJsonSchema(s.items);
|
|
73
|
+
return s;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Recursively removes JSON Schema keywords that Google Gemini's `responseSchema`
|
|
77
|
+
* (an OpenAPI 3.0 subset) rejects — notably `additionalProperties`, which the
|
|
78
|
+
* API returns a 400 for. Mutates and returns the schema.
|
|
79
|
+
* @param s - A JSON Schema object (mutated in place)
|
|
80
|
+
* @returns The same object, accepted by Gemini's `responseSchema`
|
|
81
|
+
*/
|
|
82
|
+
const sanitizeGeminiSchema = (s) => {
|
|
83
|
+
if (!s || typeof s !== "object")
|
|
84
|
+
return s;
|
|
85
|
+
delete s.additionalProperties;
|
|
86
|
+
if (s.properties) {
|
|
87
|
+
for (const key of Object.keys(s.properties)) {
|
|
88
|
+
sanitizeGeminiSchema(s.properties[key]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (s.items)
|
|
92
|
+
sanitizeGeminiSchema(s.items);
|
|
93
|
+
return s;
|
|
94
|
+
};
|
|
26
95
|
const checkIfAIProviderConfigured = (config) => {
|
|
27
96
|
if (!config ||
|
|
28
97
|
!config.openAIAPIKey ||
|
|
@@ -469,28 +538,102 @@ const getOpenAICompatibleProvider = (modelName, config) => {
|
|
|
469
538
|
}
|
|
470
539
|
return null;
|
|
471
540
|
};
|
|
541
|
+
/**
|
|
542
|
+
* Whether a model accepts the `temperature` sampling parameter at all. Some
|
|
543
|
+
* models reject it with an HTTP 400, so it must be omitted rather than clamped:
|
|
544
|
+
* - Anthropic removed sampling params (temperature/top_p/top_k) on Claude
|
|
545
|
+
* Opus 4.7 and later (Opus 4.7/4.8) and on the 5-series (Sonnet 5, Fable 5,
|
|
546
|
+
* Mythos 5). Opus 4.6, Sonnet 4.6, claude-3-5-sonnet, and Haiku 4.5 still
|
|
547
|
+
* accept it.
|
|
548
|
+
* - OpenAI reasoning models (the GPT-5 family and the o-series) reject it;
|
|
549
|
+
* gpt-4o still accepts it.
|
|
550
|
+
* DeepSeek, Kimi/Moonshot, GLM, and Gemini all accept temperature.
|
|
551
|
+
* @param modelName - the model identifier being routed
|
|
552
|
+
* @returns `true` when the model accepts a `temperature` parameter
|
|
553
|
+
*/
|
|
554
|
+
const modelSupportsTemperature = (modelName) => {
|
|
555
|
+
if (!modelName)
|
|
556
|
+
return false;
|
|
557
|
+
// OpenAI reasoning families reject sampling params (gpt-5*, o1/o3/...).
|
|
558
|
+
if (/^gpt-5/.test(modelName) || /^o\d/.test(modelName))
|
|
559
|
+
return false;
|
|
560
|
+
// Anthropic dropped sampling params on Opus 4.7+ and the 5-series.
|
|
561
|
+
if (/^claude-opus-4-(7|8|9|\d\d)\b/.test(modelName))
|
|
562
|
+
return false;
|
|
563
|
+
if (/^claude-(sonnet|opus|haiku)-5\b/.test(modelName))
|
|
564
|
+
return false;
|
|
565
|
+
if (/^claude-(fable|mythos)-5\b/.test(modelName))
|
|
566
|
+
return false;
|
|
567
|
+
return true;
|
|
568
|
+
};
|
|
569
|
+
/**
|
|
570
|
+
* Per-provider valid `temperature` range, used to clamp caller-supplied values.
|
|
571
|
+
* Anthropic, Kimi (Moonshot), and GLM (Zhipu) cap at 1.0; OpenAI, Gemini, and
|
|
572
|
+
* DeepSeek accept up to 2.0. Only consulted for models that support temperature
|
|
573
|
+
* at all (see `modelSupportsTemperature`).
|
|
574
|
+
*/
|
|
575
|
+
const getTemperatureRange = (modelName) => {
|
|
576
|
+
if (modelName.startsWith("claude-") ||
|
|
577
|
+
modelName.startsWith("kimi-") ||
|
|
578
|
+
modelName.startsWith("moonshot-") ||
|
|
579
|
+
modelName.startsWith("glm-")) {
|
|
580
|
+
return { min: 0, max: 1 };
|
|
581
|
+
}
|
|
582
|
+
// gpt-, gemini-, deepseek-
|
|
583
|
+
return { min: 0, max: 2 };
|
|
584
|
+
};
|
|
585
|
+
/**
|
|
586
|
+
* Resolves the `temperature` model setting for a given model. Applied in order:
|
|
587
|
+
* 1. presence/type — only a finite number is forwarded (`temperature: 0` is
|
|
588
|
+
* valid; `undefined`/`null`/non-numeric leaves the provider default);
|
|
589
|
+
* 2. support — models that reject the parameter (Opus 4.7+, GPT-5/o-series)
|
|
590
|
+
* get it omitted, with a warning, to avoid an HTTP 400;
|
|
591
|
+
* 3. range — values outside the provider's range are clamped, with a warning.
|
|
592
|
+
* Returns an object to spread into the LangChain model settings (`{}` when the
|
|
593
|
+
* field should be omitted).
|
|
594
|
+
*/
|
|
595
|
+
const resolveTemperatureSetting = (modelName, config) => {
|
|
596
|
+
const temperature = config?.temperature;
|
|
597
|
+
if (typeof temperature !== "number" || !Number.isFinite(temperature)) {
|
|
598
|
+
return {};
|
|
599
|
+
}
|
|
600
|
+
if (!modelSupportsTemperature(modelName)) {
|
|
601
|
+
logger_js_1.default.log(null, logger_js_1.default.levels.warn, `Model "${modelName}" does not accept a temperature parameter; ignoring configured temperature ${temperature}.`);
|
|
602
|
+
return {};
|
|
603
|
+
}
|
|
604
|
+
const { min, max } = getTemperatureRange(modelName);
|
|
605
|
+
const clamped = Math.min(Math.max(temperature, min), max);
|
|
606
|
+
if (clamped !== temperature) {
|
|
607
|
+
logger_js_1.default.log(null, logger_js_1.default.levels.warn, `Temperature ${temperature} is outside the supported range [${min}, ${max}] for "${modelName}"; clamping to ${clamped}.`);
|
|
608
|
+
}
|
|
609
|
+
return { temperature: clamped };
|
|
610
|
+
};
|
|
472
611
|
/**
|
|
473
612
|
* Instantiates and returns the appropriate LangChain chat model based on the model
|
|
474
613
|
* name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
|
|
475
614
|
* `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
|
|
476
615
|
* Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
|
|
477
616
|
* through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
|
|
478
|
-
* `schema` is provided
|
|
479
|
-
*
|
|
480
|
-
* model
|
|
617
|
+
* `schema` is provided, native structured output is configured per provider: OpenAI
|
|
618
|
+
* via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
|
|
619
|
+
* Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
|
|
620
|
+
* emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
|
|
621
|
+
* The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
|
|
622
|
+
* have no native structured-output support here. Throws a `BAD_REQUEST` error for
|
|
623
|
+
* unrecognised model names.
|
|
481
624
|
* @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
|
|
482
625
|
* `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
|
|
483
626
|
* @param config - Configuration object containing API keys (`openAIAPIKey`,
|
|
484
627
|
* `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
|
|
485
628
|
* optional `maxTokens`, and optional `temperature`
|
|
486
|
-
* @param schema - Optional Zod schema used to configure structured JSON output
|
|
487
|
-
*
|
|
488
|
-
* @returns A configured LangChain chat model instance
|
|
629
|
+
* @param schema - Optional Zod schema used to configure native structured JSON output
|
|
630
|
+
* for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
|
|
631
|
+
* @returns A configured LangChain chat model (or bound runnable) instance
|
|
489
632
|
*/
|
|
490
633
|
const getLLMModel = (modelName, config, schema = null) => {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
634
|
+
// Resolve `temperature` with presence/support/range handling (see
|
|
635
|
+
// resolveTemperatureSetting). Applied uniformly to every provider below.
|
|
636
|
+
const modelSettings = resolveTemperatureSetting(modelName, config);
|
|
494
637
|
// Claude models (Anthropic)
|
|
495
638
|
if (modelName.startsWith("claude-")) {
|
|
496
639
|
// Anthropic's SDK rejects non-streamed requests when max_tokens is large
|
|
@@ -499,23 +642,53 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
499
642
|
// through the streaming endpoint internally — callers see no interface
|
|
500
643
|
// change, but the guard is bypassed.
|
|
501
644
|
const maxTokens = config.maxTokens || 16384;
|
|
645
|
+
// Native structured output: Anthropic's `output_config.format` constrains
|
|
646
|
+
// decoding to the schema while still emitting JSON as the message text, so
|
|
647
|
+
// the downstream streaming + parse/validate pipeline is unchanged.
|
|
648
|
+
// `transformJSONSchema` applies the same strict-schema normalisation
|
|
649
|
+
// (additionalProperties: false, required fields) that @langchain/anthropic
|
|
650
|
+
// uses internally, which the API requires.
|
|
651
|
+
const outputConfig = schema
|
|
652
|
+
? {
|
|
653
|
+
outputConfig: {
|
|
654
|
+
format: {
|
|
655
|
+
type: "json_schema",
|
|
656
|
+
schema: (0, transform_json_schema_1.transformJSONSchema)(zod_1.z.toJSONSchema(schema)),
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
}
|
|
660
|
+
: {};
|
|
502
661
|
return new anthropic_1.ChatAnthropic({
|
|
503
662
|
apiKey: config.anthropicAPIKey,
|
|
504
663
|
maxTokens,
|
|
505
664
|
modelName: modelName,
|
|
506
665
|
streaming: maxTokens > 16384,
|
|
507
666
|
invocationKwargs: { cache_control: { type: "ephemeral" } },
|
|
667
|
+
...outputConfig,
|
|
508
668
|
...modelSettings,
|
|
509
669
|
});
|
|
510
670
|
}
|
|
511
671
|
// Gemini models (Google)
|
|
512
672
|
else if (modelName.startsWith("gemini-")) {
|
|
513
|
-
|
|
673
|
+
const model = new google_genai_1.ChatGoogleGenerativeAI({
|
|
514
674
|
apiKey: config.googleAPIKey,
|
|
515
675
|
maxOutputTokens: config.maxTokens || 200000,
|
|
516
676
|
model: modelName,
|
|
677
|
+
// `json: true` forces responseMimeType=application/json, guaranteeing the
|
|
678
|
+
// model returns valid JSON text (never prose) for the parse pipeline.
|
|
679
|
+
...(schema ? { json: true } : {}),
|
|
517
680
|
...modelSettings,
|
|
518
681
|
});
|
|
682
|
+
// `responseSchema` additionally constrains the output shape. It is a
|
|
683
|
+
// call-time option (not a constructor field), so it is bound onto the model
|
|
684
|
+
// via `withConfig`. Gemini's schema is an OpenAPI 3.0 subset, so
|
|
685
|
+
// `additionalProperties` is stripped. Output stays text, so the
|
|
686
|
+
// parse/validate pipeline is unchanged.
|
|
687
|
+
if (schema) {
|
|
688
|
+
const jsonSchema = sanitizeGeminiSchema(buildJsonSchema(schema));
|
|
689
|
+
return model.withConfig({ responseSchema: jsonSchema });
|
|
690
|
+
}
|
|
691
|
+
return model;
|
|
519
692
|
}
|
|
520
693
|
// GPT models (OpenAI)
|
|
521
694
|
else if (modelName.startsWith("gpt-")) {
|
|
@@ -527,7 +700,7 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
527
700
|
};
|
|
528
701
|
// Use native response_format with JSON schema for structured output
|
|
529
702
|
if (schema) {
|
|
530
|
-
const jsonSchema = (
|
|
703
|
+
const jsonSchema = strictifyJsonSchema(buildJsonSchema(schema));
|
|
531
704
|
openAISettings.modelKwargs = {
|
|
532
705
|
response_format: {
|
|
533
706
|
type: "json_schema",
|
|
@@ -1027,7 +1200,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1027
1200
|
}
|
|
1028
1201
|
if (onProgressReport) {
|
|
1029
1202
|
// Streaming mode: use server-side chunk-based progress for all models
|
|
1030
|
-
const useNativeSchema = expectsJsonResponse && schema && modelName
|
|
1203
|
+
const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
|
|
1031
1204
|
const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
|
|
1032
1205
|
// Build messages with JSON instructions if needed
|
|
1033
1206
|
let messagesToSend;
|
|
@@ -1036,7 +1209,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1036
1209
|
? "Respond with valid JSON."
|
|
1037
1210
|
: JSON_ESCAPE_INSTRUCTION;
|
|
1038
1211
|
if (schema && !useNativeSchema) {
|
|
1039
|
-
const jsonSchema = (
|
|
1212
|
+
const jsonSchema = buildJsonSchema(schema);
|
|
1040
1213
|
systemContent += `\n\nYour response MUST conform to this JSON schema:\n${JSON.stringify(jsonSchema, null, 2)}`;
|
|
1041
1214
|
}
|
|
1042
1215
|
if (messages.length > 0 && messages[0].role === "system") {
|
|
@@ -1174,17 +1347,22 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1174
1347
|
}
|
|
1175
1348
|
}
|
|
1176
1349
|
else {
|
|
1177
|
-
// Non-streaming mode: use native
|
|
1178
|
-
|
|
1350
|
+
// Non-streaming mode: use native structured output when the provider
|
|
1351
|
+
// supports it (OpenAI/Anthropic/Gemini); otherwise fall back to prompt
|
|
1352
|
+
// instructions + validation retry.
|
|
1353
|
+
const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
|
|
1179
1354
|
const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
|
|
1180
|
-
// Add escape instruction to help LLM produce valid JSON (only if expecting JSON)
|
|
1181
|
-
//
|
|
1355
|
+
// Add escape instruction to help LLM produce valid JSON (only if expecting JSON).
|
|
1356
|
+
// When native structured output is in use the model is already constrained,
|
|
1357
|
+
// so a light instruction suffices and the schema is not re-injected.
|
|
1182
1358
|
let messagesToSend;
|
|
1183
1359
|
if (expectsJsonResponse) {
|
|
1184
|
-
let systemContent =
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1360
|
+
let systemContent = useNativeSchema
|
|
1361
|
+
? "Respond with valid JSON."
|
|
1362
|
+
: JSON_ESCAPE_INSTRUCTION;
|
|
1363
|
+
// Include the schema in the prompt only when not using native output.
|
|
1364
|
+
if (schema && !useNativeSchema) {
|
|
1365
|
+
const jsonSchema = buildJsonSchema(schema);
|
|
1188
1366
|
systemContent += `\n\nYour response MUST conform to this JSON schema:\n${JSON.stringify(jsonSchema, null, 2)}`;
|
|
1189
1367
|
}
|
|
1190
1368
|
if (messages.length > 0 && messages[0].role === "system") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAuGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;wBAktB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBA0BG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCA4XF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA//B8B,GAAG,KAAG,MAAM;+CAprB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAosBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAsiCT,wBASE"}
|
package/dist/esm/langChain.js
CHANGED
|
@@ -7,7 +7,8 @@ import constants from "./const.js";
|
|
|
7
7
|
import { createAgent as createLangChainAgent } from "langchain";
|
|
8
8
|
import errorHandlingHelper from "./errorHandling.js";
|
|
9
9
|
import logger from "./logger.js";
|
|
10
|
-
import {
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { transformJSONSchema } from "@anthropic-ai/sdk/lib/transform-json-schema";
|
|
11
12
|
import { getAbortSignal } from "./runtimeContext.js";
|
|
12
13
|
const JSON_ESCAPE_INSTRUCTION = `
|
|
13
14
|
CRITICAL - Your response must be valid JSON. Escape ALL special characters in string values:
|
|
@@ -18,6 +19,74 @@ CRITICAL - Your response must be valid JSON. Escape ALL special characters in st
|
|
|
18
19
|
- Backslashes → \\\\
|
|
19
20
|
Do NOT include raw newlines, tabs, or unescaped quotes inside JSON string values.
|
|
20
21
|
`.trim();
|
|
22
|
+
/**
|
|
23
|
+
* Providers for which `getLLMModel` configures native, schema-constrained JSON
|
|
24
|
+
* output (OpenAI `response_format`, Anthropic `output_config.format`, Gemini
|
|
25
|
+
* `responseSchema` + JSON mode). For these the model emits JSON directly, so the
|
|
26
|
+
* prompt-injected schema and the schema-validation retry are skipped. Other
|
|
27
|
+
* providers (DeepSeek/Kimi/GLM via the OpenAI-compatible shim) have no native
|
|
28
|
+
* support here and continue to rely on prompt instructions + validation.
|
|
29
|
+
* @param modelName - The model identifier being routed
|
|
30
|
+
* @returns `true` when native structured output is wired up for the provider
|
|
31
|
+
*/
|
|
32
|
+
const supportsNativeSchema = (modelName) => modelName.startsWith("gpt-") ||
|
|
33
|
+
modelName.startsWith("claude-") ||
|
|
34
|
+
modelName.startsWith("gemini-");
|
|
35
|
+
/**
|
|
36
|
+
* Converts a Zod v4 schema to a JSON Schema using zod's built-in
|
|
37
|
+
* `z.toJSONSchema`. The classic `zod-to-json-schema` package only understands
|
|
38
|
+
* zod v3 and silently returns `{}` for v4 schemas, which would send empty
|
|
39
|
+
* schemas to every provider. Targets OpenAPI 3.0 — the dialect omits the
|
|
40
|
+
* `$schema` keyword and is the widest-compatible across providers — and inlines
|
|
41
|
+
* reused subschemas so the result carries no `$ref`/`$defs` (which Gemini's
|
|
42
|
+
* `responseSchema` rejects).
|
|
43
|
+
* @param schema - A Zod schema
|
|
44
|
+
* @returns The equivalent JSON Schema object
|
|
45
|
+
*/
|
|
46
|
+
const buildJsonSchema = (schema) => z.toJSONSchema(schema, { target: "openapi-3.0", reused: "inline" });
|
|
47
|
+
/**
|
|
48
|
+
* Recursively enforces OpenAI strict structured-output rules on a JSON Schema:
|
|
49
|
+
* every object gets `additionalProperties: false` and lists all of its
|
|
50
|
+
* properties in `required`. OpenAI's `strict: true` mode rejects optional
|
|
51
|
+
* properties, so all fields are marked required (matching OpenAI's own schema
|
|
52
|
+
* transform). Mutates and returns the schema.
|
|
53
|
+
* @param s - A JSON Schema object (mutated in place)
|
|
54
|
+
* @returns The same object, made strict-compatible
|
|
55
|
+
*/
|
|
56
|
+
const strictifyJsonSchema = (s) => {
|
|
57
|
+
if (!s || typeof s !== "object")
|
|
58
|
+
return s;
|
|
59
|
+
if (s.type === "object" && s.properties) {
|
|
60
|
+
s.additionalProperties = false;
|
|
61
|
+
s.required = Object.keys(s.properties);
|
|
62
|
+
for (const key of Object.keys(s.properties)) {
|
|
63
|
+
strictifyJsonSchema(s.properties[key]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (s.type === "array" && s.items)
|
|
67
|
+
strictifyJsonSchema(s.items);
|
|
68
|
+
return s;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Recursively removes JSON Schema keywords that Google Gemini's `responseSchema`
|
|
72
|
+
* (an OpenAPI 3.0 subset) rejects — notably `additionalProperties`, which the
|
|
73
|
+
* API returns a 400 for. Mutates and returns the schema.
|
|
74
|
+
* @param s - A JSON Schema object (mutated in place)
|
|
75
|
+
* @returns The same object, accepted by Gemini's `responseSchema`
|
|
76
|
+
*/
|
|
77
|
+
const sanitizeGeminiSchema = (s) => {
|
|
78
|
+
if (!s || typeof s !== "object")
|
|
79
|
+
return s;
|
|
80
|
+
delete s.additionalProperties;
|
|
81
|
+
if (s.properties) {
|
|
82
|
+
for (const key of Object.keys(s.properties)) {
|
|
83
|
+
sanitizeGeminiSchema(s.properties[key]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (s.items)
|
|
87
|
+
sanitizeGeminiSchema(s.items);
|
|
88
|
+
return s;
|
|
89
|
+
};
|
|
21
90
|
const checkIfAIProviderConfigured = (config) => {
|
|
22
91
|
if (!config ||
|
|
23
92
|
!config.openAIAPIKey ||
|
|
@@ -464,28 +533,102 @@ const getOpenAICompatibleProvider = (modelName, config) => {
|
|
|
464
533
|
}
|
|
465
534
|
return null;
|
|
466
535
|
};
|
|
536
|
+
/**
|
|
537
|
+
* Whether a model accepts the `temperature` sampling parameter at all. Some
|
|
538
|
+
* models reject it with an HTTP 400, so it must be omitted rather than clamped:
|
|
539
|
+
* - Anthropic removed sampling params (temperature/top_p/top_k) on Claude
|
|
540
|
+
* Opus 4.7 and later (Opus 4.7/4.8) and on the 5-series (Sonnet 5, Fable 5,
|
|
541
|
+
* Mythos 5). Opus 4.6, Sonnet 4.6, claude-3-5-sonnet, and Haiku 4.5 still
|
|
542
|
+
* accept it.
|
|
543
|
+
* - OpenAI reasoning models (the GPT-5 family and the o-series) reject it;
|
|
544
|
+
* gpt-4o still accepts it.
|
|
545
|
+
* DeepSeek, Kimi/Moonshot, GLM, and Gemini all accept temperature.
|
|
546
|
+
* @param modelName - the model identifier being routed
|
|
547
|
+
* @returns `true` when the model accepts a `temperature` parameter
|
|
548
|
+
*/
|
|
549
|
+
const modelSupportsTemperature = (modelName) => {
|
|
550
|
+
if (!modelName)
|
|
551
|
+
return false;
|
|
552
|
+
// OpenAI reasoning families reject sampling params (gpt-5*, o1/o3/...).
|
|
553
|
+
if (/^gpt-5/.test(modelName) || /^o\d/.test(modelName))
|
|
554
|
+
return false;
|
|
555
|
+
// Anthropic dropped sampling params on Opus 4.7+ and the 5-series.
|
|
556
|
+
if (/^claude-opus-4-(7|8|9|\d\d)\b/.test(modelName))
|
|
557
|
+
return false;
|
|
558
|
+
if (/^claude-(sonnet|opus|haiku)-5\b/.test(modelName))
|
|
559
|
+
return false;
|
|
560
|
+
if (/^claude-(fable|mythos)-5\b/.test(modelName))
|
|
561
|
+
return false;
|
|
562
|
+
return true;
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* Per-provider valid `temperature` range, used to clamp caller-supplied values.
|
|
566
|
+
* Anthropic, Kimi (Moonshot), and GLM (Zhipu) cap at 1.0; OpenAI, Gemini, and
|
|
567
|
+
* DeepSeek accept up to 2.0. Only consulted for models that support temperature
|
|
568
|
+
* at all (see `modelSupportsTemperature`).
|
|
569
|
+
*/
|
|
570
|
+
const getTemperatureRange = (modelName) => {
|
|
571
|
+
if (modelName.startsWith("claude-") ||
|
|
572
|
+
modelName.startsWith("kimi-") ||
|
|
573
|
+
modelName.startsWith("moonshot-") ||
|
|
574
|
+
modelName.startsWith("glm-")) {
|
|
575
|
+
return { min: 0, max: 1 };
|
|
576
|
+
}
|
|
577
|
+
// gpt-, gemini-, deepseek-
|
|
578
|
+
return { min: 0, max: 2 };
|
|
579
|
+
};
|
|
580
|
+
/**
|
|
581
|
+
* Resolves the `temperature` model setting for a given model. Applied in order:
|
|
582
|
+
* 1. presence/type — only a finite number is forwarded (`temperature: 0` is
|
|
583
|
+
* valid; `undefined`/`null`/non-numeric leaves the provider default);
|
|
584
|
+
* 2. support — models that reject the parameter (Opus 4.7+, GPT-5/o-series)
|
|
585
|
+
* get it omitted, with a warning, to avoid an HTTP 400;
|
|
586
|
+
* 3. range — values outside the provider's range are clamped, with a warning.
|
|
587
|
+
* Returns an object to spread into the LangChain model settings (`{}` when the
|
|
588
|
+
* field should be omitted).
|
|
589
|
+
*/
|
|
590
|
+
const resolveTemperatureSetting = (modelName, config) => {
|
|
591
|
+
const temperature = config?.temperature;
|
|
592
|
+
if (typeof temperature !== "number" || !Number.isFinite(temperature)) {
|
|
593
|
+
return {};
|
|
594
|
+
}
|
|
595
|
+
if (!modelSupportsTemperature(modelName)) {
|
|
596
|
+
logger.log(null, logger.levels.warn, `Model "${modelName}" does not accept a temperature parameter; ignoring configured temperature ${temperature}.`);
|
|
597
|
+
return {};
|
|
598
|
+
}
|
|
599
|
+
const { min, max } = getTemperatureRange(modelName);
|
|
600
|
+
const clamped = Math.min(Math.max(temperature, min), max);
|
|
601
|
+
if (clamped !== temperature) {
|
|
602
|
+
logger.log(null, logger.levels.warn, `Temperature ${temperature} is outside the supported range [${min}, ${max}] for "${modelName}"; clamping to ${clamped}.`);
|
|
603
|
+
}
|
|
604
|
+
return { temperature: clamped };
|
|
605
|
+
};
|
|
467
606
|
/**
|
|
468
607
|
* Instantiates and returns the appropriate LangChain chat model based on the model
|
|
469
608
|
* name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
|
|
470
609
|
* `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
|
|
471
610
|
* Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
|
|
472
611
|
* through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
|
|
473
|
-
* `schema` is provided
|
|
474
|
-
*
|
|
475
|
-
* model
|
|
612
|
+
* `schema` is provided, native structured output is configured per provider: OpenAI
|
|
613
|
+
* via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
|
|
614
|
+
* Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
|
|
615
|
+
* emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
|
|
616
|
+
* The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
|
|
617
|
+
* have no native structured-output support here. Throws a `BAD_REQUEST` error for
|
|
618
|
+
* unrecognised model names.
|
|
476
619
|
* @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
|
|
477
620
|
* `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
|
|
478
621
|
* @param config - Configuration object containing API keys (`openAIAPIKey`,
|
|
479
622
|
* `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
|
|
480
623
|
* optional `maxTokens`, and optional `temperature`
|
|
481
|
-
* @param schema - Optional Zod schema used to configure structured JSON output
|
|
482
|
-
*
|
|
483
|
-
* @returns A configured LangChain chat model instance
|
|
624
|
+
* @param schema - Optional Zod schema used to configure native structured JSON output
|
|
625
|
+
* for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
|
|
626
|
+
* @returns A configured LangChain chat model (or bound runnable) instance
|
|
484
627
|
*/
|
|
485
628
|
const getLLMModel = (modelName, config, schema = null) => {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
629
|
+
// Resolve `temperature` with presence/support/range handling (see
|
|
630
|
+
// resolveTemperatureSetting). Applied uniformly to every provider below.
|
|
631
|
+
const modelSettings = resolveTemperatureSetting(modelName, config);
|
|
489
632
|
// Claude models (Anthropic)
|
|
490
633
|
if (modelName.startsWith("claude-")) {
|
|
491
634
|
// Anthropic's SDK rejects non-streamed requests when max_tokens is large
|
|
@@ -494,23 +637,53 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
494
637
|
// through the streaming endpoint internally — callers see no interface
|
|
495
638
|
// change, but the guard is bypassed.
|
|
496
639
|
const maxTokens = config.maxTokens || 16384;
|
|
640
|
+
// Native structured output: Anthropic's `output_config.format` constrains
|
|
641
|
+
// decoding to the schema while still emitting JSON as the message text, so
|
|
642
|
+
// the downstream streaming + parse/validate pipeline is unchanged.
|
|
643
|
+
// `transformJSONSchema` applies the same strict-schema normalisation
|
|
644
|
+
// (additionalProperties: false, required fields) that @langchain/anthropic
|
|
645
|
+
// uses internally, which the API requires.
|
|
646
|
+
const outputConfig = schema
|
|
647
|
+
? {
|
|
648
|
+
outputConfig: {
|
|
649
|
+
format: {
|
|
650
|
+
type: "json_schema",
|
|
651
|
+
schema: transformJSONSchema(z.toJSONSchema(schema)),
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
}
|
|
655
|
+
: {};
|
|
497
656
|
return new ChatAnthropic({
|
|
498
657
|
apiKey: config.anthropicAPIKey,
|
|
499
658
|
maxTokens,
|
|
500
659
|
modelName: modelName,
|
|
501
660
|
streaming: maxTokens > 16384,
|
|
502
661
|
invocationKwargs: { cache_control: { type: "ephemeral" } },
|
|
662
|
+
...outputConfig,
|
|
503
663
|
...modelSettings,
|
|
504
664
|
});
|
|
505
665
|
}
|
|
506
666
|
// Gemini models (Google)
|
|
507
667
|
else if (modelName.startsWith("gemini-")) {
|
|
508
|
-
|
|
668
|
+
const model = new ChatGoogleGenerativeAI({
|
|
509
669
|
apiKey: config.googleAPIKey,
|
|
510
670
|
maxOutputTokens: config.maxTokens || 200000,
|
|
511
671
|
model: modelName,
|
|
672
|
+
// `json: true` forces responseMimeType=application/json, guaranteeing the
|
|
673
|
+
// model returns valid JSON text (never prose) for the parse pipeline.
|
|
674
|
+
...(schema ? { json: true } : {}),
|
|
512
675
|
...modelSettings,
|
|
513
676
|
});
|
|
677
|
+
// `responseSchema` additionally constrains the output shape. It is a
|
|
678
|
+
// call-time option (not a constructor field), so it is bound onto the model
|
|
679
|
+
// via `withConfig`. Gemini's schema is an OpenAPI 3.0 subset, so
|
|
680
|
+
// `additionalProperties` is stripped. Output stays text, so the
|
|
681
|
+
// parse/validate pipeline is unchanged.
|
|
682
|
+
if (schema) {
|
|
683
|
+
const jsonSchema = sanitizeGeminiSchema(buildJsonSchema(schema));
|
|
684
|
+
return model.withConfig({ responseSchema: jsonSchema });
|
|
685
|
+
}
|
|
686
|
+
return model;
|
|
514
687
|
}
|
|
515
688
|
// GPT models (OpenAI)
|
|
516
689
|
else if (modelName.startsWith("gpt-")) {
|
|
@@ -522,7 +695,7 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
522
695
|
};
|
|
523
696
|
// Use native response_format with JSON schema for structured output
|
|
524
697
|
if (schema) {
|
|
525
|
-
const jsonSchema =
|
|
698
|
+
const jsonSchema = strictifyJsonSchema(buildJsonSchema(schema));
|
|
526
699
|
openAISettings.modelKwargs = {
|
|
527
700
|
response_format: {
|
|
528
701
|
type: "json_schema",
|
|
@@ -1022,7 +1195,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1022
1195
|
}
|
|
1023
1196
|
if (onProgressReport) {
|
|
1024
1197
|
// Streaming mode: use server-side chunk-based progress for all models
|
|
1025
|
-
const useNativeSchema = expectsJsonResponse && schema && modelName
|
|
1198
|
+
const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
|
|
1026
1199
|
const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
|
|
1027
1200
|
// Build messages with JSON instructions if needed
|
|
1028
1201
|
let messagesToSend;
|
|
@@ -1031,7 +1204,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1031
1204
|
? "Respond with valid JSON."
|
|
1032
1205
|
: JSON_ESCAPE_INSTRUCTION;
|
|
1033
1206
|
if (schema && !useNativeSchema) {
|
|
1034
|
-
const jsonSchema =
|
|
1207
|
+
const jsonSchema = buildJsonSchema(schema);
|
|
1035
1208
|
systemContent += `\n\nYour response MUST conform to this JSON schema:\n${JSON.stringify(jsonSchema, null, 2)}`;
|
|
1036
1209
|
}
|
|
1037
1210
|
if (messages.length > 0 && messages[0].role === "system") {
|
|
@@ -1169,17 +1342,22 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1169
1342
|
}
|
|
1170
1343
|
}
|
|
1171
1344
|
else {
|
|
1172
|
-
// Non-streaming mode: use native
|
|
1173
|
-
|
|
1345
|
+
// Non-streaming mode: use native structured output when the provider
|
|
1346
|
+
// supports it (OpenAI/Anthropic/Gemini); otherwise fall back to prompt
|
|
1347
|
+
// instructions + validation retry.
|
|
1348
|
+
const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
|
|
1174
1349
|
const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
|
|
1175
|
-
// Add escape instruction to help LLM produce valid JSON (only if expecting JSON)
|
|
1176
|
-
//
|
|
1350
|
+
// Add escape instruction to help LLM produce valid JSON (only if expecting JSON).
|
|
1351
|
+
// When native structured output is in use the model is already constrained,
|
|
1352
|
+
// so a light instruction suffices and the schema is not re-injected.
|
|
1177
1353
|
let messagesToSend;
|
|
1178
1354
|
if (expectsJsonResponse) {
|
|
1179
|
-
let systemContent =
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1355
|
+
let systemContent = useNativeSchema
|
|
1356
|
+
? "Respond with valid JSON."
|
|
1357
|
+
: JSON_ESCAPE_INSTRUCTION;
|
|
1358
|
+
// Include the schema in the prompt only when not using native output.
|
|
1359
|
+
if (schema && !useNativeSchema) {
|
|
1360
|
+
const jsonSchema = buildJsonSchema(schema);
|
|
1183
1361
|
systemContent += `\n\nYour response MUST conform to this JSON schema:\n${JSON.stringify(jsonSchema, null, 2)}`;
|
|
1184
1362
|
}
|
|
1185
1363
|
if (messages.length > 0 && messages[0].role === "system") {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.2.
|
|
6
|
+
"version": "1.2.14",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/cjs/index.js",
|
|
9
9
|
"module": "dist/esm/index.js",
|
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"clean": "rm -rf dist",
|
|
16
|
-
"build": "npm run clean && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && echo '{\"type\":\"module\"}' > dist/esm/package.json"
|
|
16
|
+
"build": "npm run clean && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && echo '{\"type\":\"module\"}' > dist/esm/package.json",
|
|
17
|
+
"work:start": "bash scripts/work-start.sh",
|
|
18
|
+
"work:commit": "bash scripts/work-commit.sh",
|
|
19
|
+
"work:release": "bash scripts/release.sh",
|
|
20
|
+
"release": "bash scripts/release.sh",
|
|
21
|
+
"release:fanout": "bash scripts/release-fanout.sh"
|
|
17
22
|
},
|
|
18
23
|
"repository": {
|
|
19
24
|
"type": "git",
|
|
@@ -51,8 +56,7 @@
|
|
|
51
56
|
"mongoose": "^9.3.1",
|
|
52
57
|
"openai": "^6.32.0",
|
|
53
58
|
"winston": "^3.19.0",
|
|
54
|
-
"zod": "^4.3.6"
|
|
55
|
-
"zod-to-json-schema": "^3.25.1"
|
|
59
|
+
"zod": "^4.3.6"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
62
|
"typescript": "^5.9.3"
|