elsium-ai 0.2.3 → 0.3.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/README.md +73 -42
- package/dist/index.d.ts +14 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1370 -223
- package/package.json +12 -11
package/dist/index.js
CHANGED
|
@@ -511,6 +511,243 @@ function envBool(name, fallback) {
|
|
|
511
511
|
metadata: { variable: name }
|
|
512
512
|
});
|
|
513
513
|
}
|
|
514
|
+
// ../core/src/schema.ts
|
|
515
|
+
var log = createLogger();
|
|
516
|
+
function zodDefKind(def) {
|
|
517
|
+
return typeof def.type === "string" ? def.type : def.typeName;
|
|
518
|
+
}
|
|
519
|
+
function zodObjectToJsonSchema(schema, convert) {
|
|
520
|
+
const shape = typeof schema.shape === "function" ? schema.shape() : schema.shape;
|
|
521
|
+
const properties = {};
|
|
522
|
+
const required = [];
|
|
523
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
524
|
+
const fieldSchema = value;
|
|
525
|
+
properties[key] = convert(fieldSchema);
|
|
526
|
+
const fieldDef = fieldSchema._def;
|
|
527
|
+
const fieldKind = zodDefKind(fieldDef);
|
|
528
|
+
if (fieldKind !== "optional" && fieldKind !== "ZodOptional" && fieldKind !== "default" && fieldKind !== "ZodDefault") {
|
|
529
|
+
required.push(key);
|
|
530
|
+
}
|
|
531
|
+
if (fieldDef.description) {
|
|
532
|
+
properties[key].description = fieldDef.description;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return { type: "object", properties, required };
|
|
536
|
+
}
|
|
537
|
+
function zodToJsonSchema(schema) {
|
|
538
|
+
if (!("_def" in schema))
|
|
539
|
+
return { type: "object" };
|
|
540
|
+
const def = schema._def;
|
|
541
|
+
const kind = zodDefKind(def);
|
|
542
|
+
switch (kind) {
|
|
543
|
+
case "object":
|
|
544
|
+
case "ZodObject":
|
|
545
|
+
return zodObjectToJsonSchema(def, zodToJsonSchema);
|
|
546
|
+
case "string":
|
|
547
|
+
case "ZodString":
|
|
548
|
+
return { type: "string" };
|
|
549
|
+
case "number":
|
|
550
|
+
case "ZodNumber":
|
|
551
|
+
return { type: "number" };
|
|
552
|
+
case "boolean":
|
|
553
|
+
case "ZodBoolean":
|
|
554
|
+
return { type: "boolean" };
|
|
555
|
+
case "array":
|
|
556
|
+
case "ZodArray":
|
|
557
|
+
return {
|
|
558
|
+
type: "array",
|
|
559
|
+
items: zodToJsonSchema(def.element ?? def.type)
|
|
560
|
+
};
|
|
561
|
+
case "enum":
|
|
562
|
+
case "ZodEnum": {
|
|
563
|
+
const values = def.values ?? (def.entries ? Object.values(def.entries) : []);
|
|
564
|
+
return { type: "string", enum: values };
|
|
565
|
+
}
|
|
566
|
+
case "optional":
|
|
567
|
+
case "ZodOptional":
|
|
568
|
+
return zodToJsonSchema(def.innerType);
|
|
569
|
+
case "default":
|
|
570
|
+
case "ZodDefault":
|
|
571
|
+
return zodToJsonSchema(def.innerType);
|
|
572
|
+
case "nullable":
|
|
573
|
+
case "ZodNullable": {
|
|
574
|
+
const inner = zodToJsonSchema(def.innerType);
|
|
575
|
+
return { ...inner, nullable: true };
|
|
576
|
+
}
|
|
577
|
+
case "ZodLiteral":
|
|
578
|
+
return { type: typeof def.value, const: def.value };
|
|
579
|
+
case "ZodUnion": {
|
|
580
|
+
const options = def.options.map(zodToJsonSchema);
|
|
581
|
+
return { anyOf: options };
|
|
582
|
+
}
|
|
583
|
+
case "ZodRecord":
|
|
584
|
+
return {
|
|
585
|
+
type: "object",
|
|
586
|
+
additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : { type: "string" }
|
|
587
|
+
};
|
|
588
|
+
case "ZodTuple": {
|
|
589
|
+
const items = (def.items ?? []).map(zodToJsonSchema);
|
|
590
|
+
return { type: "array", prefixItems: items, minItems: items.length, maxItems: items.length };
|
|
591
|
+
}
|
|
592
|
+
case "ZodDate":
|
|
593
|
+
return { type: "string", format: "date-time" };
|
|
594
|
+
default:
|
|
595
|
+
log.warn(`zodToJsonSchema: unsupported type ${kind}, defaulting to string`);
|
|
596
|
+
return { type: "string" };
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// ../core/src/registry.ts
|
|
600
|
+
var log2 = createLogger();
|
|
601
|
+
var BLOCKED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
602
|
+
function createRegistry(label) {
|
|
603
|
+
const entries = new Map;
|
|
604
|
+
return {
|
|
605
|
+
register(name, factory) {
|
|
606
|
+
if (BLOCKED_KEYS.has(name)) {
|
|
607
|
+
log2.warn(`Registry(${label}): rejected blocked key "${name}"`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
entries.set(name, factory);
|
|
611
|
+
log2.debug(`Registry(${label}): registered "${name}"`);
|
|
612
|
+
},
|
|
613
|
+
get(name) {
|
|
614
|
+
if (BLOCKED_KEYS.has(name))
|
|
615
|
+
return;
|
|
616
|
+
return entries.get(name);
|
|
617
|
+
},
|
|
618
|
+
list() {
|
|
619
|
+
return Array.from(entries.keys());
|
|
620
|
+
},
|
|
621
|
+
has(name) {
|
|
622
|
+
if (BLOCKED_KEYS.has(name))
|
|
623
|
+
return false;
|
|
624
|
+
return entries.has(name);
|
|
625
|
+
},
|
|
626
|
+
unregister(name) {
|
|
627
|
+
if (BLOCKED_KEYS.has(name))
|
|
628
|
+
return false;
|
|
629
|
+
const deleted = entries.delete(name);
|
|
630
|
+
if (deleted) {
|
|
631
|
+
log2.debug(`Registry(${label}): unregistered "${name}"`);
|
|
632
|
+
}
|
|
633
|
+
return deleted;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
// ../core/src/tokens.ts
|
|
638
|
+
var MODEL_RATIOS = {
|
|
639
|
+
cl100k_base: 4,
|
|
640
|
+
"gpt-4o": 4,
|
|
641
|
+
"gpt-4o-mini": 4,
|
|
642
|
+
o1: 4,
|
|
643
|
+
"o1-mini": 4,
|
|
644
|
+
"o3-mini": 4,
|
|
645
|
+
"claude-opus-4-6": 3.5,
|
|
646
|
+
"claude-sonnet-4-6": 3.5,
|
|
647
|
+
"claude-haiku-4-5-20251001": 3.5,
|
|
648
|
+
"gemini-2.0-flash": 4,
|
|
649
|
+
"gemini-2.0-flash-lite": 4,
|
|
650
|
+
"gemini-2.5-pro-preview-05-06": 4,
|
|
651
|
+
"gemini-2.5-flash-preview-04-17": 4
|
|
652
|
+
};
|
|
653
|
+
function getRatio(model) {
|
|
654
|
+
if (!model)
|
|
655
|
+
return 4;
|
|
656
|
+
if (MODEL_RATIOS[model])
|
|
657
|
+
return MODEL_RATIOS[model];
|
|
658
|
+
if (model.startsWith("claude"))
|
|
659
|
+
return 3.5;
|
|
660
|
+
if (model.startsWith("gemini"))
|
|
661
|
+
return 4;
|
|
662
|
+
if (model.startsWith("gpt") || model.startsWith("o1") || model.startsWith("o3"))
|
|
663
|
+
return 4;
|
|
664
|
+
return 4;
|
|
665
|
+
}
|
|
666
|
+
function extractMessageText(msg) {
|
|
667
|
+
if (typeof msg.content === "string")
|
|
668
|
+
return msg.content;
|
|
669
|
+
return msg.content.filter((p) => p.type === "text" && ("text" in p)).map((p) => p.text).join("");
|
|
670
|
+
}
|
|
671
|
+
function countTokens(text, model) {
|
|
672
|
+
const ratio = getRatio(model);
|
|
673
|
+
return Math.ceil(text.length / ratio) + 4;
|
|
674
|
+
}
|
|
675
|
+
function createContextManager(config) {
|
|
676
|
+
const { maxTokens, strategy, reserveTokens = 0 } = config;
|
|
677
|
+
const budget = maxTokens - reserveTokens;
|
|
678
|
+
function estimateMessageTokens(msg) {
|
|
679
|
+
return countTokens(extractMessageText(msg)) + 4;
|
|
680
|
+
}
|
|
681
|
+
function estimateTokens(messages) {
|
|
682
|
+
return messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);
|
|
683
|
+
}
|
|
684
|
+
async function fitTruncate(messages, system) {
|
|
685
|
+
let available = budget;
|
|
686
|
+
if (system)
|
|
687
|
+
available -= countTokens(system);
|
|
688
|
+
const result = [];
|
|
689
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
690
|
+
const tokens = estimateMessageTokens(messages[i]);
|
|
691
|
+
if (available - tokens < 0 && result.length > 0)
|
|
692
|
+
break;
|
|
693
|
+
available -= tokens;
|
|
694
|
+
result.unshift(messages[i]);
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|
|
698
|
+
async function fitSummarize(messages, system) {
|
|
699
|
+
if (!config.summarizer)
|
|
700
|
+
return fitTruncate(messages, system);
|
|
701
|
+
let available = budget;
|
|
702
|
+
if (system)
|
|
703
|
+
available -= countTokens(system);
|
|
704
|
+
const total = estimateTokens(messages);
|
|
705
|
+
if (total <= available)
|
|
706
|
+
return messages;
|
|
707
|
+
const keepCount = Math.max(1, Math.floor(messages.length / 3));
|
|
708
|
+
const toSummarize = messages.slice(0, messages.length - keepCount);
|
|
709
|
+
const toKeep = messages.slice(messages.length - keepCount);
|
|
710
|
+
const summary = await config.summarizer(toSummarize);
|
|
711
|
+
const summaryMsg = {
|
|
712
|
+
role: "system",
|
|
713
|
+
content: `Previous conversation summary: ${summary}`
|
|
714
|
+
};
|
|
715
|
+
return [summaryMsg, ...toKeep];
|
|
716
|
+
}
|
|
717
|
+
async function fitSlidingWindow(messages, system) {
|
|
718
|
+
let available = budget;
|
|
719
|
+
if (system)
|
|
720
|
+
available -= countTokens(system);
|
|
721
|
+
const result = [];
|
|
722
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
723
|
+
const tokens = estimateMessageTokens(messages[i]);
|
|
724
|
+
if (available - tokens < 0 && result.length > 0)
|
|
725
|
+
break;
|
|
726
|
+
available -= tokens;
|
|
727
|
+
result.unshift(messages[i]);
|
|
728
|
+
}
|
|
729
|
+
return result;
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
estimateTokens,
|
|
733
|
+
async fit(messages, system) {
|
|
734
|
+
const total = estimateTokens(messages);
|
|
735
|
+
let available = budget;
|
|
736
|
+
if (system)
|
|
737
|
+
available -= countTokens(system);
|
|
738
|
+
if (total <= available)
|
|
739
|
+
return messages;
|
|
740
|
+
switch (strategy) {
|
|
741
|
+
case "truncate":
|
|
742
|
+
return fitTruncate(messages, system);
|
|
743
|
+
case "summarize":
|
|
744
|
+
return fitSummarize(messages, system);
|
|
745
|
+
case "sliding-window":
|
|
746
|
+
return fitSlidingWindow(messages, system);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
514
751
|
// ../core/src/circuit-breaker.ts
|
|
515
752
|
function defaultShouldCount(error) {
|
|
516
753
|
if (error && typeof error === "object" && "retryable" in error) {
|
|
@@ -763,16 +1000,16 @@ function composeMiddleware(middlewares) {
|
|
|
763
1000
|
};
|
|
764
1001
|
}
|
|
765
1002
|
function loggingMiddleware(logger) {
|
|
766
|
-
const
|
|
1003
|
+
const log3 = logger ?? createLogger({ level: "info" });
|
|
767
1004
|
return async (ctx, next) => {
|
|
768
|
-
|
|
1005
|
+
log3.info("LLM request", {
|
|
769
1006
|
provider: ctx.provider,
|
|
770
1007
|
model: ctx.model,
|
|
771
1008
|
traceId: ctx.traceId,
|
|
772
1009
|
messageCount: ctx.request.messages.length
|
|
773
1010
|
});
|
|
774
1011
|
const response = await next(ctx);
|
|
775
|
-
|
|
1012
|
+
log3.info("LLM response", {
|
|
776
1013
|
provider: ctx.provider,
|
|
777
1014
|
model: ctx.model,
|
|
778
1015
|
traceId: ctx.traceId,
|
|
@@ -908,7 +1145,7 @@ function xrayMiddleware(options = {}) {
|
|
|
908
1145
|
}
|
|
909
1146
|
|
|
910
1147
|
// ../gateway/src/pricing.ts
|
|
911
|
-
var
|
|
1148
|
+
var log3 = createLogger();
|
|
912
1149
|
var PRICING = {
|
|
913
1150
|
"claude-opus-4-6": { inputPerMillion: 15, outputPerMillion: 75 },
|
|
914
1151
|
"claude-sonnet-4-6": { inputPerMillion: 3, outputPerMillion: 15 },
|
|
@@ -946,7 +1183,7 @@ function resolveModelName(model) {
|
|
|
946
1183
|
function calculateCost(model, usage) {
|
|
947
1184
|
const pricing = PRICING[resolveModelName(model)];
|
|
948
1185
|
if (!pricing) {
|
|
949
|
-
|
|
1186
|
+
log3.warn(`Unknown model "${model}" — cost will be reported as $0. Register pricing with registerPricing().`);
|
|
950
1187
|
return {
|
|
951
1188
|
inputCost: 0,
|
|
952
1189
|
outputCost: 0,
|
|
@@ -1040,15 +1277,33 @@ function createAnthropicProvider(config) {
|
|
|
1040
1277
|
if (part.type === "text")
|
|
1041
1278
|
return { type: "text", text: part.text };
|
|
1042
1279
|
if (part.type === "image" && part.source?.type === "base64") {
|
|
1280
|
+
const src = part.source;
|
|
1043
1281
|
return {
|
|
1044
1282
|
type: "image",
|
|
1045
1283
|
source: {
|
|
1046
1284
|
type: "base64",
|
|
1047
|
-
media_type:
|
|
1048
|
-
data:
|
|
1285
|
+
media_type: src.mediaType,
|
|
1286
|
+
data: src.data
|
|
1049
1287
|
}
|
|
1050
1288
|
};
|
|
1051
1289
|
}
|
|
1290
|
+
if (part.type === "document" && part.source) {
|
|
1291
|
+
if (part.source.type === "base64") {
|
|
1292
|
+
const src = part.source;
|
|
1293
|
+
return {
|
|
1294
|
+
type: "document",
|
|
1295
|
+
source: {
|
|
1296
|
+
type: "base64",
|
|
1297
|
+
media_type: src.mediaType,
|
|
1298
|
+
data: src.data
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
return { type: "text", text: "[document: url source not supported by Anthropic]" };
|
|
1303
|
+
}
|
|
1304
|
+
if (part.type === "audio") {
|
|
1305
|
+
return { type: "text", text: "[audio content not supported by this provider]" };
|
|
1306
|
+
}
|
|
1052
1307
|
return { type: "text", text: "[unsupported content]" };
|
|
1053
1308
|
}
|
|
1054
1309
|
function formatMultipartContent(msg, role) {
|
|
@@ -1157,6 +1412,16 @@ function createAnthropicProvider(config) {
|
|
|
1157
1412
|
const tools = formatTools(req.tools);
|
|
1158
1413
|
if (tools)
|
|
1159
1414
|
body.tools = tools;
|
|
1415
|
+
if (req.schema) {
|
|
1416
|
+
const jsonSchema = zodToJsonSchema(req.schema);
|
|
1417
|
+
const structuredTool = {
|
|
1418
|
+
name: "_structured_output",
|
|
1419
|
+
description: "Return structured output matching the required schema",
|
|
1420
|
+
input_schema: jsonSchema
|
|
1421
|
+
};
|
|
1422
|
+
body.tools = [...tools ?? [], structuredTool];
|
|
1423
|
+
body.tool_choice = { type: "tool", name: "_structured_output" };
|
|
1424
|
+
}
|
|
1160
1425
|
const startTime = performance.now();
|
|
1161
1426
|
const raw = await retry(async () => {
|
|
1162
1427
|
const controller = new AbortController;
|
|
@@ -1377,6 +1642,18 @@ function createGoogleProvider(config) {
|
|
|
1377
1642
|
} else {
|
|
1378
1643
|
parts.push({ fileData: { mimeType: "image/jpeg", fileUri: img.source.url } });
|
|
1379
1644
|
}
|
|
1645
|
+
} else if (p.type === "audio" || p.type === "document") {
|
|
1646
|
+
const media = p;
|
|
1647
|
+
if (media.source.type === "base64") {
|
|
1648
|
+
parts.push({
|
|
1649
|
+
inlineData: { mimeType: media.source.mediaType, data: media.source.data }
|
|
1650
|
+
});
|
|
1651
|
+
} else {
|
|
1652
|
+
const urlSource = media.source;
|
|
1653
|
+
parts.push({
|
|
1654
|
+
fileData: { mimeType: "application/octet-stream", fileUri: urlSource.url }
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1380
1657
|
}
|
|
1381
1658
|
}
|
|
1382
1659
|
return { role, parts };
|
|
@@ -1475,6 +1752,10 @@ function createGoogleProvider(config) {
|
|
|
1475
1752
|
config2.topP = req.topP;
|
|
1476
1753
|
if (req.stopSequences?.length)
|
|
1477
1754
|
config2.stopSequences = req.stopSequences;
|
|
1755
|
+
if (req.schema) {
|
|
1756
|
+
config2.responseMimeType = "application/json";
|
|
1757
|
+
config2.responseSchema = zodToJsonSchema(req.schema);
|
|
1758
|
+
}
|
|
1478
1759
|
return config2;
|
|
1479
1760
|
}
|
|
1480
1761
|
function buildRequestBody(req) {
|
|
@@ -1754,6 +2035,25 @@ function createOpenAIProvider(config) {
|
|
|
1754
2035
|
} else {
|
|
1755
2036
|
parts.push({ type: "image_url", image_url: { url: part.source.url } });
|
|
1756
2037
|
}
|
|
2038
|
+
} else if (part.type === "audio") {
|
|
2039
|
+
if (part.source.type === "base64") {
|
|
2040
|
+
const format = part.source.mediaType.split("/")[1] ?? "wav";
|
|
2041
|
+
parts.push({
|
|
2042
|
+
type: "input_audio",
|
|
2043
|
+
input_audio: { data: part.source.data, format }
|
|
2044
|
+
});
|
|
2045
|
+
} else {
|
|
2046
|
+
parts.push({ type: "text", text: "[audio: url source requires file upload]" });
|
|
2047
|
+
}
|
|
2048
|
+
} else if (part.type === "document") {
|
|
2049
|
+
if (part.source.type === "base64") {
|
|
2050
|
+
parts.push({
|
|
2051
|
+
type: "text",
|
|
2052
|
+
text: `[document: ${part.source.mediaType} content attached as base64]`
|
|
2053
|
+
});
|
|
2054
|
+
} else {
|
|
2055
|
+
parts.push({ type: "text", text: `[document: ${part.source.url}]` });
|
|
2056
|
+
}
|
|
1757
2057
|
}
|
|
1758
2058
|
}
|
|
1759
2059
|
return parts;
|
|
@@ -1850,6 +2150,17 @@ function createOpenAIProvider(config) {
|
|
|
1850
2150
|
const tools = formatTools(req.tools);
|
|
1851
2151
|
if (tools)
|
|
1852
2152
|
body.tools = tools;
|
|
2153
|
+
if (req.schema) {
|
|
2154
|
+
const jsonSchema = zodToJsonSchema(req.schema);
|
|
2155
|
+
body.response_format = {
|
|
2156
|
+
type: "json_schema",
|
|
2157
|
+
json_schema: {
|
|
2158
|
+
name: "structured_output",
|
|
2159
|
+
strict: true,
|
|
2160
|
+
schema: jsonSchema
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
1853
2164
|
const startTime = performance.now();
|
|
1854
2165
|
const raw = await retry(async () => {
|
|
1855
2166
|
const controller = new AbortController;
|
|
@@ -2163,107 +2474,46 @@ function gateway(config) {
|
|
|
2163
2474
|
},
|
|
2164
2475
|
async generate(request) {
|
|
2165
2476
|
const { schema, ...rest } = request;
|
|
2166
|
-
const jsonSchema =
|
|
2167
|
-
const systemPrompt = [
|
|
2168
|
-
rest.system ?? "",
|
|
2169
|
-
"You MUST respond with valid JSON matching this schema:",
|
|
2170
|
-
JSON.stringify(jsonSchema, null, 2),
|
|
2171
|
-
"Respond ONLY with the JSON object, no markdown or explanation."
|
|
2172
|
-
].filter(Boolean).join(`
|
|
2173
|
-
|
|
2174
|
-
`);
|
|
2477
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
2175
2478
|
const response = await executeWithMiddleware({
|
|
2176
2479
|
...rest,
|
|
2177
|
-
|
|
2480
|
+
schema,
|
|
2481
|
+
system: [
|
|
2482
|
+
rest.system ?? "",
|
|
2483
|
+
"You MUST respond with valid JSON matching this schema:",
|
|
2484
|
+
JSON.stringify(jsonSchema, null, 2),
|
|
2485
|
+
"Respond ONLY with the JSON object, no markdown or explanation."
|
|
2486
|
+
].filter(Boolean).join(`
|
|
2487
|
+
|
|
2488
|
+
`)
|
|
2178
2489
|
});
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
}
|
|
2490
|
+
let parsed;
|
|
2491
|
+
if (response.stopReason === "tool_use" && response.message.toolCalls?.length) {
|
|
2492
|
+
const structuredCall = response.message.toolCalls.find((tc) => tc.name === "_structured_output");
|
|
2493
|
+
if (structuredCall) {
|
|
2494
|
+
parsed = structuredCall.arguments;
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
if (parsed === undefined) {
|
|
2498
|
+
const text = typeof response.message.content === "string" ? response.message.content : "";
|
|
2499
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
2500
|
+
if (!jsonMatch) {
|
|
2501
|
+
throw ElsiumError.validation("LLM response did not contain valid JSON", {
|
|
2502
|
+
response: text
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
parsed = JSON.parse(jsonMatch[0]);
|
|
2185
2506
|
}
|
|
2186
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
2187
2507
|
const result = schema.safeParse(parsed);
|
|
2188
2508
|
if (!result.success) {
|
|
2189
2509
|
throw ElsiumError.validation("LLM response did not match schema", {
|
|
2190
|
-
errors: result.error.issues
|
|
2191
|
-
response: text
|
|
2510
|
+
errors: result.error.issues
|
|
2192
2511
|
});
|
|
2193
2512
|
}
|
|
2194
2513
|
return { data: result.data, response };
|
|
2195
2514
|
}
|
|
2196
2515
|
};
|
|
2197
2516
|
}
|
|
2198
|
-
function schemaToJsonSchema(schema) {
|
|
2199
|
-
try {
|
|
2200
|
-
if ("_def" in schema) {
|
|
2201
|
-
const def = schema._def;
|
|
2202
|
-
const result = convertZodDef(def);
|
|
2203
|
-
if (result)
|
|
2204
|
-
return result;
|
|
2205
|
-
}
|
|
2206
|
-
} catch {}
|
|
2207
|
-
return { type: "string" };
|
|
2208
|
-
}
|
|
2209
|
-
function zodDefKind(def) {
|
|
2210
|
-
return typeof def.type === "string" ? def.type : def.typeName;
|
|
2211
|
-
}
|
|
2212
|
-
function convertZodDef(def) {
|
|
2213
|
-
const kind = zodDefKind(def);
|
|
2214
|
-
switch (kind) {
|
|
2215
|
-
case "object":
|
|
2216
|
-
case "ZodObject":
|
|
2217
|
-
return convertZodObject(def);
|
|
2218
|
-
case "string":
|
|
2219
|
-
case "ZodString":
|
|
2220
|
-
return { type: "string" };
|
|
2221
|
-
case "number":
|
|
2222
|
-
case "ZodNumber":
|
|
2223
|
-
return { type: "number" };
|
|
2224
|
-
case "boolean":
|
|
2225
|
-
case "ZodBoolean":
|
|
2226
|
-
return { type: "boolean" };
|
|
2227
|
-
case "array":
|
|
2228
|
-
case "ZodArray":
|
|
2229
|
-
return convertZodArray(def);
|
|
2230
|
-
case "enum":
|
|
2231
|
-
case "ZodEnum": {
|
|
2232
|
-
const values = def.values ?? (def.entries ? Object.values(def.entries) : []);
|
|
2233
|
-
return { type: "string", enum: values };
|
|
2234
|
-
}
|
|
2235
|
-
case "optional":
|
|
2236
|
-
case "ZodOptional":
|
|
2237
|
-
return convertZodOptional(def);
|
|
2238
|
-
default:
|
|
2239
|
-
return null;
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
function convertZodObject(def) {
|
|
2243
|
-
if (!def.shape)
|
|
2244
|
-
return null;
|
|
2245
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
2246
|
-
const properties = {};
|
|
2247
|
-
const required = [];
|
|
2248
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
2249
|
-
properties[key] = schemaToJsonSchema(value);
|
|
2250
|
-
const valDef = value._def;
|
|
2251
|
-
const valKind = zodDefKind(valDef);
|
|
2252
|
-
if (valKind !== "optional" && valKind !== "ZodOptional") {
|
|
2253
|
-
required.push(key);
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
return { type: "object", properties, required };
|
|
2257
|
-
}
|
|
2258
|
-
function convertZodArray(def) {
|
|
2259
|
-
return {
|
|
2260
|
-
type: "array",
|
|
2261
|
-
items: schemaToJsonSchema(def.element ?? def.type)
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
function convertZodOptional(def) {
|
|
2265
|
-
return schemaToJsonSchema(def.innerType ?? def.innerType);
|
|
2266
|
-
}
|
|
2267
2517
|
// ../gateway/src/security.ts
|
|
2268
2518
|
var INJECTION_PATTERNS = [
|
|
2269
2519
|
{
|
|
@@ -2514,6 +2764,321 @@ function securityMiddleware(config) {
|
|
|
2514
2764
|
return response;
|
|
2515
2765
|
};
|
|
2516
2766
|
}
|
|
2767
|
+
// ../gateway/src/cache.ts
|
|
2768
|
+
import { createHash } from "node:crypto";
|
|
2769
|
+
var log4 = createLogger();
|
|
2770
|
+
function createInMemoryCache(maxSize = 1000) {
|
|
2771
|
+
const cache = new Map;
|
|
2772
|
+
function evict() {
|
|
2773
|
+
if (cache.size <= maxSize)
|
|
2774
|
+
return;
|
|
2775
|
+
const firstKey = cache.keys().next().value;
|
|
2776
|
+
if (firstKey !== undefined)
|
|
2777
|
+
cache.delete(firstKey);
|
|
2778
|
+
}
|
|
2779
|
+
return {
|
|
2780
|
+
async get(key) {
|
|
2781
|
+
const entry = cache.get(key);
|
|
2782
|
+
if (!entry)
|
|
2783
|
+
return null;
|
|
2784
|
+
if (Date.now() > entry.expiresAt) {
|
|
2785
|
+
cache.delete(key);
|
|
2786
|
+
return null;
|
|
2787
|
+
}
|
|
2788
|
+
cache.delete(key);
|
|
2789
|
+
cache.set(key, entry);
|
|
2790
|
+
return entry.value;
|
|
2791
|
+
},
|
|
2792
|
+
async set(key, value, ttlMs) {
|
|
2793
|
+
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
2794
|
+
evict();
|
|
2795
|
+
},
|
|
2796
|
+
async delete(key) {
|
|
2797
|
+
cache.delete(key);
|
|
2798
|
+
},
|
|
2799
|
+
async clear() {
|
|
2800
|
+
cache.clear();
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
function defaultCacheKey(ctx) {
|
|
2805
|
+
const data = JSON.stringify({
|
|
2806
|
+
provider: ctx.provider,
|
|
2807
|
+
model: ctx.model,
|
|
2808
|
+
messages: ctx.request.messages,
|
|
2809
|
+
system: ctx.request.system,
|
|
2810
|
+
temperature: ctx.request.temperature
|
|
2811
|
+
});
|
|
2812
|
+
return createHash("sha256").update(data).digest("hex");
|
|
2813
|
+
}
|
|
2814
|
+
function defaultShouldCache(_ctx, response) {
|
|
2815
|
+
const temp = _ctx.request.temperature;
|
|
2816
|
+
if (temp !== undefined && temp !== 0)
|
|
2817
|
+
return false;
|
|
2818
|
+
return response.stopReason === "end_turn";
|
|
2819
|
+
}
|
|
2820
|
+
function cacheMiddleware(config) {
|
|
2821
|
+
const ttlMs = config?.ttlMs ?? 3600000;
|
|
2822
|
+
const adapter = config?.adapter ?? createInMemoryCache(config?.maxSize ?? 1000);
|
|
2823
|
+
const keyFn = config?.keyFn ?? defaultCacheKey;
|
|
2824
|
+
const shouldCache = config?.shouldCache ?? defaultShouldCache;
|
|
2825
|
+
let hits = 0;
|
|
2826
|
+
let misses = 0;
|
|
2827
|
+
const middleware = async (ctx, next) => {
|
|
2828
|
+
if (ctx.request.stream) {
|
|
2829
|
+
return next(ctx);
|
|
2830
|
+
}
|
|
2831
|
+
const key = keyFn(ctx);
|
|
2832
|
+
const cached = await adapter.get(key);
|
|
2833
|
+
if (cached) {
|
|
2834
|
+
hits++;
|
|
2835
|
+
log4.debug("Cache hit", { key: key.slice(0, 8), provider: ctx.provider });
|
|
2836
|
+
return cached;
|
|
2837
|
+
}
|
|
2838
|
+
misses++;
|
|
2839
|
+
const response = await next(ctx);
|
|
2840
|
+
if (shouldCache(ctx, response)) {
|
|
2841
|
+
await adapter.set(key, response, ttlMs);
|
|
2842
|
+
}
|
|
2843
|
+
return response;
|
|
2844
|
+
};
|
|
2845
|
+
return Object.assign(middleware, {
|
|
2846
|
+
adapter,
|
|
2847
|
+
stats() {
|
|
2848
|
+
const total = hits + misses;
|
|
2849
|
+
return {
|
|
2850
|
+
hits,
|
|
2851
|
+
misses,
|
|
2852
|
+
size: 0,
|
|
2853
|
+
hitRate: total > 0 ? hits / total : 0
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
// ../gateway/src/output-guardrails.ts
|
|
2859
|
+
var log5 = createLogger();
|
|
2860
|
+
var PII_PATTERNS2 = [
|
|
2861
|
+
{
|
|
2862
|
+
pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
2863
|
+
label: "email",
|
|
2864
|
+
replacement: "[REDACTED_EMAIL]"
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
pattern: /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
2868
|
+
label: "phone",
|
|
2869
|
+
replacement: "[REDACTED_PHONE]"
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
2873
|
+
label: "ssn",
|
|
2874
|
+
replacement: "[REDACTED_SSN]"
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g,
|
|
2878
|
+
label: "credit_card",
|
|
2879
|
+
replacement: "[REDACTED_CC]"
|
|
2880
|
+
}
|
|
2881
|
+
];
|
|
2882
|
+
var SECRET_PATTERNS2 = [
|
|
2883
|
+
{
|
|
2884
|
+
pattern: /\bsk-[A-Za-z0-9]{20,}\b/g,
|
|
2885
|
+
label: "api_key",
|
|
2886
|
+
replacement: "[REDACTED_API_KEY]"
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
pattern: /\bpk-[A-Za-z0-9]{20,}\b/g,
|
|
2890
|
+
label: "api_key",
|
|
2891
|
+
replacement: "[REDACTED_API_KEY]"
|
|
2892
|
+
},
|
|
2893
|
+
{
|
|
2894
|
+
pattern: /\bAKIA[A-Z0-9]{16}\b/g,
|
|
2895
|
+
label: "aws_key",
|
|
2896
|
+
replacement: "[REDACTED_AWS_KEY]"
|
|
2897
|
+
}
|
|
2898
|
+
];
|
|
2899
|
+
function detectPII(text) {
|
|
2900
|
+
const violations = [];
|
|
2901
|
+
const normalized = text.normalize("NFKC");
|
|
2902
|
+
for (const { pattern, label } of [...PII_PATTERNS2, ...SECRET_PATTERNS2]) {
|
|
2903
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
2904
|
+
if (regex.test(normalized)) {
|
|
2905
|
+
violations.push({
|
|
2906
|
+
type: "pii",
|
|
2907
|
+
detail: `Detected ${label} in output`,
|
|
2908
|
+
pattern: label
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
return violations;
|
|
2913
|
+
}
|
|
2914
|
+
function redactPII(text) {
|
|
2915
|
+
let result = text;
|
|
2916
|
+
for (const { pattern, replacement } of [...PII_PATTERNS2, ...SECRET_PATTERNS2]) {
|
|
2917
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
2918
|
+
result = result.replace(regex, replacement);
|
|
2919
|
+
}
|
|
2920
|
+
return result;
|
|
2921
|
+
}
|
|
2922
|
+
function checkContentPolicy(text, policy) {
|
|
2923
|
+
const violations = [];
|
|
2924
|
+
if (policy.maxResponseLength && text.length > policy.maxResponseLength) {
|
|
2925
|
+
violations.push({
|
|
2926
|
+
type: "content_policy",
|
|
2927
|
+
detail: `Response length ${text.length} exceeds max ${policy.maxResponseLength}`
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
if (policy.blockedPatterns) {
|
|
2931
|
+
for (const pattern of policy.blockedPatterns) {
|
|
2932
|
+
if (pattern.test(text)) {
|
|
2933
|
+
violations.push({
|
|
2934
|
+
type: "content_policy",
|
|
2935
|
+
detail: `Response matches blocked pattern: ${pattern.source}`,
|
|
2936
|
+
pattern: pattern.source
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
return violations;
|
|
2942
|
+
}
|
|
2943
|
+
function checkCustomRules(text, rules) {
|
|
2944
|
+
const violations = [];
|
|
2945
|
+
for (const rule of rules) {
|
|
2946
|
+
if (rule.pattern.test(text)) {
|
|
2947
|
+
violations.push({
|
|
2948
|
+
type: "custom_rule",
|
|
2949
|
+
detail: rule.message ?? `Output matched custom rule: ${rule.name}`,
|
|
2950
|
+
pattern: rule.pattern.source
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return violations;
|
|
2955
|
+
}
|
|
2956
|
+
function outputGuardrailMiddleware(config) {
|
|
2957
|
+
const mode = config.onViolation ?? "block";
|
|
2958
|
+
return async (ctx, next) => {
|
|
2959
|
+
const response = await next(ctx);
|
|
2960
|
+
const text = extractText(response.message.content);
|
|
2961
|
+
const violations = [];
|
|
2962
|
+
if (config.piiDetection) {
|
|
2963
|
+
violations.push(...detectPII(text));
|
|
2964
|
+
}
|
|
2965
|
+
if (config.contentPolicy) {
|
|
2966
|
+
violations.push(...checkContentPolicy(text, config.contentPolicy));
|
|
2967
|
+
}
|
|
2968
|
+
if (config.customRules?.length) {
|
|
2969
|
+
violations.push(...checkCustomRules(text, config.customRules));
|
|
2970
|
+
}
|
|
2971
|
+
if (violations.length === 0)
|
|
2972
|
+
return response;
|
|
2973
|
+
for (const v of violations) {
|
|
2974
|
+
config.onViolationCallback?.(v);
|
|
2975
|
+
}
|
|
2976
|
+
switch (mode) {
|
|
2977
|
+
case "block":
|
|
2978
|
+
throw ElsiumError.validation(`Output guardrail violation: ${violations.map((v) => v.detail).join("; ")}`, { violations });
|
|
2979
|
+
case "redact": {
|
|
2980
|
+
let redacted = text;
|
|
2981
|
+
if (config.piiDetection) {
|
|
2982
|
+
redacted = redactPII(redacted);
|
|
2983
|
+
}
|
|
2984
|
+
return {
|
|
2985
|
+
...response,
|
|
2986
|
+
message: { ...response.message, content: redacted }
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
case "warn":
|
|
2990
|
+
log5.warn("Output guardrail violations detected", { violations });
|
|
2991
|
+
return response;
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
// ../gateway/src/batch.ts
|
|
2996
|
+
var log6 = createLogger();
|
|
2997
|
+
function createBatch(gateway2, config) {
|
|
2998
|
+
const concurrency = config?.concurrency ?? 5;
|
|
2999
|
+
const retryPerItem = config?.retryPerItem ?? 0;
|
|
3000
|
+
return {
|
|
3001
|
+
async execute(requests) {
|
|
3002
|
+
const startTime = performance.now();
|
|
3003
|
+
const results = new Array(requests.length);
|
|
3004
|
+
let completed = 0;
|
|
3005
|
+
let totalSucceeded = 0;
|
|
3006
|
+
let totalFailed = 0;
|
|
3007
|
+
let running = 0;
|
|
3008
|
+
let nextIndex = 0;
|
|
3009
|
+
const signal = config?.signal;
|
|
3010
|
+
async function processItem(index) {
|
|
3011
|
+
if (signal?.aborted) {
|
|
3012
|
+
results[index] = {
|
|
3013
|
+
index,
|
|
3014
|
+
success: false,
|
|
3015
|
+
error: "Batch cancelled"
|
|
3016
|
+
};
|
|
3017
|
+
totalFailed++;
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
let lastError;
|
|
3021
|
+
for (let attempt = 0;attempt <= retryPerItem; attempt++) {
|
|
3022
|
+
try {
|
|
3023
|
+
const response = await gateway2.complete(requests[index]);
|
|
3024
|
+
results[index] = { index, success: true, response };
|
|
3025
|
+
totalSucceeded++;
|
|
3026
|
+
return;
|
|
3027
|
+
} catch (err2) {
|
|
3028
|
+
lastError = err2 instanceof Error ? err2.message : String(err2);
|
|
3029
|
+
if (attempt < retryPerItem && err2 instanceof ElsiumError && err2.retryable) {
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
break;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
results[index] = { index, success: false, error: lastError };
|
|
3036
|
+
totalFailed++;
|
|
3037
|
+
}
|
|
3038
|
+
return new Promise((resolve) => {
|
|
3039
|
+
function scheduleNext() {
|
|
3040
|
+
while (running < concurrency && nextIndex < requests.length) {
|
|
3041
|
+
if (signal?.aborted) {
|
|
3042
|
+
for (let i = nextIndex;i < requests.length; i++) {
|
|
3043
|
+
results[i] = { index: i, success: false, error: "Batch cancelled" };
|
|
3044
|
+
totalFailed++;
|
|
3045
|
+
}
|
|
3046
|
+
nextIndex = requests.length;
|
|
3047
|
+
break;
|
|
3048
|
+
}
|
|
3049
|
+
const idx = nextIndex++;
|
|
3050
|
+
running++;
|
|
3051
|
+
processItem(idx).then(() => {
|
|
3052
|
+
running--;
|
|
3053
|
+
completed++;
|
|
3054
|
+
config?.onProgress?.(completed, requests.length);
|
|
3055
|
+
if (completed === requests.length) {
|
|
3056
|
+
resolve({
|
|
3057
|
+
results,
|
|
3058
|
+
totalSucceeded,
|
|
3059
|
+
totalFailed,
|
|
3060
|
+
totalDurationMs: Math.round(performance.now() - startTime)
|
|
3061
|
+
});
|
|
3062
|
+
} else {
|
|
3063
|
+
scheduleNext();
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
if (requests.length === 0) {
|
|
3069
|
+
resolve({
|
|
3070
|
+
results: [],
|
|
3071
|
+
totalSucceeded: 0,
|
|
3072
|
+
totalFailed: 0,
|
|
3073
|
+
totalDurationMs: 0
|
|
3074
|
+
});
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
scheduleNext();
|
|
3078
|
+
});
|
|
3079
|
+
}
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
2517
3082
|
// ../gateway/src/router.ts
|
|
2518
3083
|
var REASONING_KEYWORDS = /\b(prove|explain why|analyze|compare|contrast|evaluate|critique|debate|reason|deduce|infer|justify|argue|synthesize|hypothesize|derive)\b/i;
|
|
2519
3084
|
var CODE_KEYWORDS = /\b(implement|refactor|debug|optimize|architect|design pattern|algorithm|data structure|write code|code review|fix the bug|type system)\b/i;
|
|
@@ -2762,7 +3327,6 @@ function createProviderMesh(config) {
|
|
|
2762
3327
|
};
|
|
2763
3328
|
}
|
|
2764
3329
|
// ../tools/src/define.ts
|
|
2765
|
-
var log2 = createLogger();
|
|
2766
3330
|
function defineTool(config) {
|
|
2767
3331
|
const { name, description, input, output, handler, timeoutMs = 30000 } = config;
|
|
2768
3332
|
return {
|
|
@@ -2811,114 +3375,31 @@ function defineTool(config) {
|
|
|
2811
3375
|
}
|
|
2812
3376
|
}
|
|
2813
3377
|
return {
|
|
2814
|
-
success: true,
|
|
2815
|
-
data: result,
|
|
2816
|
-
toolCallId,
|
|
2817
|
-
durationMs: Math.round(performance.now() - startTime)
|
|
2818
|
-
};
|
|
2819
|
-
} catch (error) {
|
|
2820
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2821
|
-
return {
|
|
2822
|
-
success: false,
|
|
2823
|
-
error: message,
|
|
2824
|
-
toolCallId,
|
|
2825
|
-
durationMs: Math.round(performance.now() - startTime)
|
|
2826
|
-
};
|
|
2827
|
-
} finally {
|
|
2828
|
-
clearTimeout(timer);
|
|
2829
|
-
}
|
|
2830
|
-
},
|
|
2831
|
-
toDefinition() {
|
|
2832
|
-
return {
|
|
2833
|
-
name,
|
|
2834
|
-
description,
|
|
2835
|
-
inputSchema: zodToJsonSchema(input)
|
|
2836
|
-
};
|
|
2837
|
-
}
|
|
2838
|
-
};
|
|
2839
|
-
}
|
|
2840
|
-
function zodDefKind2(def) {
|
|
2841
|
-
return typeof def.type === "string" ? def.type : def.typeName;
|
|
2842
|
-
}
|
|
2843
|
-
function zodObjectToJsonSchema(def) {
|
|
2844
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
2845
|
-
const properties = {};
|
|
2846
|
-
const required = [];
|
|
2847
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
2848
|
-
const fieldSchema = value;
|
|
2849
|
-
properties[key] = zodToJsonSchema(fieldSchema);
|
|
2850
|
-
const fieldDef = fieldSchema._def;
|
|
2851
|
-
const fieldKind = zodDefKind2(fieldDef);
|
|
2852
|
-
if (fieldKind !== "optional" && fieldKind !== "ZodOptional" && fieldKind !== "default" && fieldKind !== "ZodDefault") {
|
|
2853
|
-
required.push(key);
|
|
2854
|
-
}
|
|
2855
|
-
if (fieldDef.description) {
|
|
2856
|
-
properties[key].description = fieldDef.description;
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
return { type: "object", properties, required };
|
|
2860
|
-
}
|
|
2861
|
-
function zodToJsonSchema(schema) {
|
|
2862
|
-
if (!("_def" in schema))
|
|
2863
|
-
return { type: "object" };
|
|
2864
|
-
const def = schema._def;
|
|
2865
|
-
const kind = zodDefKind2(def);
|
|
2866
|
-
switch (kind) {
|
|
2867
|
-
case "object":
|
|
2868
|
-
case "ZodObject":
|
|
2869
|
-
return zodObjectToJsonSchema(def);
|
|
2870
|
-
case "string":
|
|
2871
|
-
case "ZodString":
|
|
2872
|
-
return { type: "string" };
|
|
2873
|
-
case "number":
|
|
2874
|
-
case "ZodNumber":
|
|
2875
|
-
return { type: "number" };
|
|
2876
|
-
case "boolean":
|
|
2877
|
-
case "ZodBoolean":
|
|
2878
|
-
return { type: "boolean" };
|
|
2879
|
-
case "array":
|
|
2880
|
-
case "ZodArray":
|
|
2881
|
-
return {
|
|
2882
|
-
type: "array",
|
|
2883
|
-
items: zodToJsonSchema(def.element ?? def.type)
|
|
2884
|
-
};
|
|
2885
|
-
case "enum":
|
|
2886
|
-
case "ZodEnum": {
|
|
2887
|
-
const values = def.values ?? (def.entries ? Object.values(def.entries) : []);
|
|
2888
|
-
return { type: "string", enum: values };
|
|
2889
|
-
}
|
|
2890
|
-
case "optional":
|
|
2891
|
-
case "ZodOptional":
|
|
2892
|
-
return zodToJsonSchema(def.innerType);
|
|
2893
|
-
case "default":
|
|
2894
|
-
case "ZodDefault":
|
|
2895
|
-
return zodToJsonSchema(def.innerType);
|
|
2896
|
-
case "nullable":
|
|
2897
|
-
case "ZodNullable": {
|
|
2898
|
-
const inner = zodToJsonSchema(def.innerType);
|
|
2899
|
-
return { ...inner, nullable: true };
|
|
2900
|
-
}
|
|
2901
|
-
case "ZodLiteral":
|
|
2902
|
-
return { type: typeof def.value, const: def.value };
|
|
2903
|
-
case "ZodUnion": {
|
|
2904
|
-
const options = def.options.map(zodToJsonSchema);
|
|
2905
|
-
return { anyOf: options };
|
|
2906
|
-
}
|
|
2907
|
-
case "ZodRecord":
|
|
3378
|
+
success: true,
|
|
3379
|
+
data: result,
|
|
3380
|
+
toolCallId,
|
|
3381
|
+
durationMs: Math.round(performance.now() - startTime)
|
|
3382
|
+
};
|
|
3383
|
+
} catch (error) {
|
|
3384
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3385
|
+
return {
|
|
3386
|
+
success: false,
|
|
3387
|
+
error: message,
|
|
3388
|
+
toolCallId,
|
|
3389
|
+
durationMs: Math.round(performance.now() - startTime)
|
|
3390
|
+
};
|
|
3391
|
+
} finally {
|
|
3392
|
+
clearTimeout(timer);
|
|
3393
|
+
}
|
|
3394
|
+
},
|
|
3395
|
+
toDefinition() {
|
|
2908
3396
|
return {
|
|
2909
|
-
|
|
2910
|
-
|
|
3397
|
+
name,
|
|
3398
|
+
description,
|
|
3399
|
+
inputSchema: zodToJsonSchema(input)
|
|
2911
3400
|
};
|
|
2912
|
-
case "ZodTuple": {
|
|
2913
|
-
const items = (def.items ?? []).map(zodToJsonSchema);
|
|
2914
|
-
return { type: "array", prefixItems: items, minItems: items.length, maxItems: items.length };
|
|
2915
3401
|
}
|
|
2916
|
-
|
|
2917
|
-
return { type: "string", format: "date-time" };
|
|
2918
|
-
default:
|
|
2919
|
-
log2.warn(`zodToJsonSchema: unsupported type ${kind}, defaulting to string`);
|
|
2920
|
-
return { type: "string" };
|
|
2921
|
-
}
|
|
3402
|
+
};
|
|
2922
3403
|
}
|
|
2923
3404
|
// ../tools/src/toolkit.ts
|
|
2924
3405
|
function createToolkit(name, tools) {
|
|
@@ -7473,15 +7954,33 @@ function createMemory(config) {
|
|
|
7473
7954
|
case "unlimited":
|
|
7474
7955
|
break;
|
|
7475
7956
|
}
|
|
7957
|
+
if (config.store && config.agentId) {
|
|
7958
|
+
config.store.save(config.agentId, [...messages]).catch(() => {});
|
|
7959
|
+
}
|
|
7476
7960
|
},
|
|
7477
7961
|
getMessages() {
|
|
7478
7962
|
return [...messages];
|
|
7479
7963
|
},
|
|
7480
7964
|
clear() {
|
|
7481
7965
|
messages.length = 0;
|
|
7966
|
+
if (config.store && config.agentId) {
|
|
7967
|
+
config.store.clear(config.agentId).catch(() => {});
|
|
7968
|
+
}
|
|
7482
7969
|
},
|
|
7483
7970
|
getTokenEstimate() {
|
|
7484
7971
|
return messages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
7972
|
+
},
|
|
7973
|
+
async loadFromStore() {
|
|
7974
|
+
if (!config.store || !config.agentId)
|
|
7975
|
+
return;
|
|
7976
|
+
const stored = await config.store.load(config.agentId);
|
|
7977
|
+
messages.length = 0;
|
|
7978
|
+
messages.push(...stored);
|
|
7979
|
+
},
|
|
7980
|
+
async saveToStore() {
|
|
7981
|
+
if (!config.store || !config.agentId)
|
|
7982
|
+
return;
|
|
7983
|
+
await config.store.save(config.agentId, [...messages]);
|
|
7485
7984
|
}
|
|
7486
7985
|
};
|
|
7487
7986
|
}
|
|
@@ -7527,7 +8026,7 @@ var JAILBREAK_PATTERNS2 = [
|
|
|
7527
8026
|
detail: "Opposite mode jailbreak attempt"
|
|
7528
8027
|
}
|
|
7529
8028
|
];
|
|
7530
|
-
var
|
|
8029
|
+
var SECRET_PATTERNS3 = [
|
|
7531
8030
|
{
|
|
7532
8031
|
pattern: /\bsk-[a-zA-Z0-9_-]{20,}\b/g,
|
|
7533
8032
|
detail: "API secret key detected",
|
|
@@ -7605,7 +8104,7 @@ function createAgentSecurity(config) {
|
|
|
7605
8104
|
const violations = [];
|
|
7606
8105
|
let redactedOutput = output;
|
|
7607
8106
|
if (config.redactSecrets !== false) {
|
|
7608
|
-
for (const { pattern, detail, replacement } of
|
|
8107
|
+
for (const { pattern, detail, replacement } of SECRET_PATTERNS3) {
|
|
7609
8108
|
const regex = new RegExp(pattern.source, pattern.flags);
|
|
7610
8109
|
if (regex.test(redactedOutput)) {
|
|
7611
8110
|
violations.push({ type: "secret_detected", detail, severity: "medium" });
|
|
@@ -8318,6 +8817,104 @@ function defineAgent(config, deps) {
|
|
|
8318
8817
|
}
|
|
8319
8818
|
};
|
|
8320
8819
|
}
|
|
8820
|
+
// ../agents/src/stores/memory-store.ts
|
|
8821
|
+
function createInMemoryMemoryStore() {
|
|
8822
|
+
const store = new Map;
|
|
8823
|
+
return {
|
|
8824
|
+
async load(agentId) {
|
|
8825
|
+
return [...store.get(agentId) ?? []];
|
|
8826
|
+
},
|
|
8827
|
+
async save(agentId, messages) {
|
|
8828
|
+
store.set(agentId, [...messages]);
|
|
8829
|
+
},
|
|
8830
|
+
async clear(agentId) {
|
|
8831
|
+
store.delete(agentId);
|
|
8832
|
+
}
|
|
8833
|
+
};
|
|
8834
|
+
}
|
|
8835
|
+
// ../agents/src/stores/sqlite-store.ts
|
|
8836
|
+
import { createRequire } from "node:module";
|
|
8837
|
+
var require2 = createRequire(import.meta.url);
|
|
8838
|
+
var log7 = createLogger();
|
|
8839
|
+
var BLOCKED_KEYS2 = new Set(["__proto__", "constructor", "prototype"]);
|
|
8840
|
+
var TABLE_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
8841
|
+
function createSqliteMemoryStore(config) {
|
|
8842
|
+
const { path, tableName = "agent_memory" } = config;
|
|
8843
|
+
if (BLOCKED_KEYS2.has(tableName)) {
|
|
8844
|
+
throw new Error(`Invalid table name: ${tableName}`);
|
|
8845
|
+
}
|
|
8846
|
+
if (!TABLE_NAME_PATTERN.test(tableName)) {
|
|
8847
|
+
throw new Error(`Invalid table name format: ${tableName}`);
|
|
8848
|
+
}
|
|
8849
|
+
let db = null;
|
|
8850
|
+
let initPromise = null;
|
|
8851
|
+
async function getDb() {
|
|
8852
|
+
if (db)
|
|
8853
|
+
return db;
|
|
8854
|
+
if (initPromise)
|
|
8855
|
+
return initPromise;
|
|
8856
|
+
initPromise = (async () => {
|
|
8857
|
+
try {
|
|
8858
|
+
const Database = require2("better-sqlite3");
|
|
8859
|
+
db = new Database(path);
|
|
8860
|
+
db.exec(`
|
|
8861
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
8862
|
+
agent_id TEXT NOT NULL,
|
|
8863
|
+
idx INTEGER NOT NULL,
|
|
8864
|
+
role TEXT NOT NULL,
|
|
8865
|
+
content TEXT NOT NULL,
|
|
8866
|
+
metadata TEXT,
|
|
8867
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
8868
|
+
PRIMARY KEY (agent_id, idx)
|
|
8869
|
+
)
|
|
8870
|
+
`);
|
|
8871
|
+
return db;
|
|
8872
|
+
} catch (err2) {
|
|
8873
|
+
initPromise = null;
|
|
8874
|
+
log7.error("Failed to initialize SQLite memory store", {
|
|
8875
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
8876
|
+
});
|
|
8877
|
+
throw new Error("better-sqlite3 is required for SQLite memory store. Install it as a dependency.");
|
|
8878
|
+
}
|
|
8879
|
+
})();
|
|
8880
|
+
return initPromise;
|
|
8881
|
+
}
|
|
8882
|
+
return {
|
|
8883
|
+
async load(agentId) {
|
|
8884
|
+
if (BLOCKED_KEYS2.has(agentId))
|
|
8885
|
+
return [];
|
|
8886
|
+
const database = await getDb();
|
|
8887
|
+
const rows = database.prepare(`SELECT role, content, metadata FROM ${tableName} WHERE agent_id = ? ORDER BY idx`).all(agentId);
|
|
8888
|
+
return rows.map((row) => {
|
|
8889
|
+
const msg = {
|
|
8890
|
+
role: row.role,
|
|
8891
|
+
content: JSON.parse(row.content)
|
|
8892
|
+
};
|
|
8893
|
+
if (row.metadata) {
|
|
8894
|
+
msg.metadata = JSON.parse(row.metadata);
|
|
8895
|
+
}
|
|
8896
|
+
return msg;
|
|
8897
|
+
});
|
|
8898
|
+
},
|
|
8899
|
+
async save(agentId, messages) {
|
|
8900
|
+
if (BLOCKED_KEYS2.has(agentId))
|
|
8901
|
+
return;
|
|
8902
|
+
const database = await getDb();
|
|
8903
|
+
database.prepare(`DELETE FROM ${tableName} WHERE agent_id = ?`).run(agentId);
|
|
8904
|
+
const insert = database.prepare(`INSERT INTO ${tableName} (agent_id, idx, role, content, metadata) VALUES (?, ?, ?, ?, ?)`);
|
|
8905
|
+
for (let i = 0;i < messages.length; i++) {
|
|
8906
|
+
const msg = messages[i];
|
|
8907
|
+
insert.run(agentId, i, msg.role, JSON.stringify(msg.content), msg.metadata ? JSON.stringify(msg.metadata) : null);
|
|
8908
|
+
}
|
|
8909
|
+
},
|
|
8910
|
+
async clear(agentId) {
|
|
8911
|
+
if (BLOCKED_KEYS2.has(agentId))
|
|
8912
|
+
return;
|
|
8913
|
+
const database = await getDb();
|
|
8914
|
+
database.prepare(`DELETE FROM ${tableName} WHERE agent_id = ?`).run(agentId);
|
|
8915
|
+
}
|
|
8916
|
+
};
|
|
8917
|
+
}
|
|
8321
8918
|
// ../agents/src/multi.ts
|
|
8322
8919
|
async function runSequential(agents, input, options) {
|
|
8323
8920
|
const results = [];
|
|
@@ -8815,7 +9412,11 @@ function createMockEmbeddings(dims = 128) {
|
|
|
8815
9412
|
}
|
|
8816
9413
|
};
|
|
8817
9414
|
}
|
|
9415
|
+
var embeddingProviderRegistry = createRegistry("embeddingProvider");
|
|
8818
9416
|
function getEmbeddingProvider(config) {
|
|
9417
|
+
const registered = embeddingProviderRegistry.get(config.provider);
|
|
9418
|
+
if (registered)
|
|
9419
|
+
return registered(config);
|
|
8819
9420
|
switch (config.provider) {
|
|
8820
9421
|
case "openai":
|
|
8821
9422
|
return createOpenAIEmbeddings(config);
|
|
@@ -8824,12 +9425,13 @@ function getEmbeddingProvider(config) {
|
|
|
8824
9425
|
default:
|
|
8825
9426
|
throw new ElsiumError({
|
|
8826
9427
|
code: "CONFIG_ERROR",
|
|
8827
|
-
message: `Unknown embedding provider: ${config.provider}`,
|
|
9428
|
+
message: `Unknown embedding provider: ${config.provider}. Available: openai, mock${embeddingProviderRegistry.list().length ? `, ${embeddingProviderRegistry.list().join(", ")}` : ""}`,
|
|
8828
9429
|
retryable: false
|
|
8829
9430
|
});
|
|
8830
9431
|
}
|
|
8831
9432
|
}
|
|
8832
9433
|
// ../rag/src/vectorstore.ts
|
|
9434
|
+
var vectorStoreRegistry = createRegistry("vectorStore");
|
|
8833
9435
|
function cosineSimilarity(a, b) {
|
|
8834
9436
|
if (a.length !== b.length)
|
|
8835
9437
|
return 0;
|
|
@@ -8963,6 +9565,123 @@ function rag(config) {
|
|
|
8963
9565
|
}
|
|
8964
9566
|
};
|
|
8965
9567
|
}
|
|
9568
|
+
// ../rag/src/stores/pgvector.ts
|
|
9569
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
9570
|
+
var require3 = createRequire2(import.meta.url);
|
|
9571
|
+
var log8 = createLogger();
|
|
9572
|
+
var BLOCKED_KEYS3 = new Set(["__proto__", "constructor", "prototype"]);
|
|
9573
|
+
var TABLE_NAME_PATTERN2 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
9574
|
+
function createPgVectorStore(config) {
|
|
9575
|
+
const { connectionString, tableName = "vector_chunks", dimensions = 1536 } = config;
|
|
9576
|
+
if (BLOCKED_KEYS3.has(tableName)) {
|
|
9577
|
+
throw new Error(`Invalid table name: ${tableName}`);
|
|
9578
|
+
}
|
|
9579
|
+
if (!TABLE_NAME_PATTERN2.test(tableName)) {
|
|
9580
|
+
throw new Error(`Invalid table name format: ${tableName}`);
|
|
9581
|
+
}
|
|
9582
|
+
let client = null;
|
|
9583
|
+
let initialized = false;
|
|
9584
|
+
async function getClient() {
|
|
9585
|
+
if (client)
|
|
9586
|
+
return client;
|
|
9587
|
+
try {
|
|
9588
|
+
const pg = require3("pg");
|
|
9589
|
+
client = new pg.Client({ connectionString });
|
|
9590
|
+
await client.connect();
|
|
9591
|
+
if (!initialized) {
|
|
9592
|
+
await client.query("CREATE EXTENSION IF NOT EXISTS vector");
|
|
9593
|
+
await client.query(`
|
|
9594
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
9595
|
+
id TEXT PRIMARY KEY,
|
|
9596
|
+
content TEXT NOT NULL,
|
|
9597
|
+
document_id TEXT NOT NULL,
|
|
9598
|
+
chunk_index INTEGER NOT NULL,
|
|
9599
|
+
metadata JSONB DEFAULT '{}',
|
|
9600
|
+
embedding vector(${dimensions})
|
|
9601
|
+
)
|
|
9602
|
+
`);
|
|
9603
|
+
initialized = true;
|
|
9604
|
+
}
|
|
9605
|
+
return client;
|
|
9606
|
+
} catch (err2) {
|
|
9607
|
+
log8.error("Failed to initialize PgVector store", {
|
|
9608
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
9609
|
+
});
|
|
9610
|
+
throw new Error("pg is required for PgVector store. Install it as a dependency.");
|
|
9611
|
+
}
|
|
9612
|
+
}
|
|
9613
|
+
return {
|
|
9614
|
+
name: "pgvector",
|
|
9615
|
+
async upsert(chunks) {
|
|
9616
|
+
const pg = await getClient();
|
|
9617
|
+
for (const chunk of chunks) {
|
|
9618
|
+
if (BLOCKED_KEYS3.has(chunk.id))
|
|
9619
|
+
continue;
|
|
9620
|
+
const embedding = `[${chunk.embedding.values.join(",")}]`;
|
|
9621
|
+
await pg.query(`INSERT INTO ${tableName} (id, content, document_id, chunk_index, metadata, embedding)
|
|
9622
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
9623
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
9624
|
+
content = EXCLUDED.content,
|
|
9625
|
+
document_id = EXCLUDED.document_id,
|
|
9626
|
+
chunk_index = EXCLUDED.chunk_index,
|
|
9627
|
+
metadata = EXCLUDED.metadata,
|
|
9628
|
+
embedding = EXCLUDED.embedding`, [
|
|
9629
|
+
chunk.id,
|
|
9630
|
+
chunk.content,
|
|
9631
|
+
chunk.documentId,
|
|
9632
|
+
chunk.index,
|
|
9633
|
+
JSON.stringify(chunk.metadata),
|
|
9634
|
+
embedding
|
|
9635
|
+
]);
|
|
9636
|
+
}
|
|
9637
|
+
},
|
|
9638
|
+
async query(embedding, options) {
|
|
9639
|
+
const pg = await getClient();
|
|
9640
|
+
const topK = options?.topK ?? 5;
|
|
9641
|
+
const minScore = options?.minScore ?? 0;
|
|
9642
|
+
const embeddingStr = `[${embedding.values.join(",")}]`;
|
|
9643
|
+
const result = await pg.query(`SELECT id, content, document_id, chunk_index, metadata,
|
|
9644
|
+
1 - (embedding <=> $1::vector) as score
|
|
9645
|
+
FROM ${tableName}
|
|
9646
|
+
WHERE 1 - (embedding <=> $1::vector) >= $2
|
|
9647
|
+
ORDER BY embedding <=> $1::vector
|
|
9648
|
+
LIMIT $3`, [embeddingStr, minScore, topK]);
|
|
9649
|
+
return result.rows.map((row) => ({
|
|
9650
|
+
chunk: {
|
|
9651
|
+
id: row.id,
|
|
9652
|
+
content: row.content,
|
|
9653
|
+
documentId: row.document_id,
|
|
9654
|
+
index: row.chunk_index,
|
|
9655
|
+
metadata: {
|
|
9656
|
+
startChar: 0,
|
|
9657
|
+
endChar: 0,
|
|
9658
|
+
tokenEstimate: 0,
|
|
9659
|
+
...row.metadata ?? {}
|
|
9660
|
+
}
|
|
9661
|
+
},
|
|
9662
|
+
score: row.score,
|
|
9663
|
+
distance: 1 - row.score
|
|
9664
|
+
}));
|
|
9665
|
+
},
|
|
9666
|
+
async delete(ids) {
|
|
9667
|
+
const pg = await getClient();
|
|
9668
|
+
const filtered = ids.filter((id) => !BLOCKED_KEYS3.has(id));
|
|
9669
|
+
if (filtered.length === 0)
|
|
9670
|
+
return;
|
|
9671
|
+
const placeholders = filtered.map((_, i) => `$${i + 1}`).join(", ");
|
|
9672
|
+
await pg.query(`DELETE FROM ${tableName} WHERE id IN (${placeholders})`, filtered);
|
|
9673
|
+
},
|
|
9674
|
+
async clear() {
|
|
9675
|
+
const pg = await getClient();
|
|
9676
|
+
await pg.query(`DELETE FROM ${tableName}`);
|
|
9677
|
+
},
|
|
9678
|
+
async count() {
|
|
9679
|
+
const pg = await getClient();
|
|
9680
|
+
const result = await pg.query(`SELECT COUNT(*)::int as count FROM ${tableName}`);
|
|
9681
|
+
return result.rows[0]?.count ?? 0;
|
|
9682
|
+
}
|
|
9683
|
+
};
|
|
9684
|
+
}
|
|
8966
9685
|
// ../workflows/src/step.ts
|
|
8967
9686
|
function step(name, config) {
|
|
8968
9687
|
return { name, ...config };
|
|
@@ -9545,7 +10264,7 @@ function createCostEngine(config = {}) {
|
|
|
9545
10264
|
}
|
|
9546
10265
|
// ../observe/src/tracer.ts
|
|
9547
10266
|
import { writeFileSync } from "node:fs";
|
|
9548
|
-
var
|
|
10267
|
+
var log9 = createLogger();
|
|
9549
10268
|
function observe(config = {}) {
|
|
9550
10269
|
const {
|
|
9551
10270
|
output = ["console"],
|
|
@@ -9568,7 +10287,7 @@ function observe(config = {}) {
|
|
|
9568
10287
|
try {
|
|
9569
10288
|
writeFileSync(filename, JSON.stringify(spansToExport, null, 2));
|
|
9570
10289
|
} catch (err2) {
|
|
9571
|
-
|
|
10290
|
+
log9.error("Failed to write trace file", {
|
|
9572
10291
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
9573
10292
|
});
|
|
9574
10293
|
}
|
|
@@ -9643,7 +10362,7 @@ function observe(config = {}) {
|
|
|
9643
10362
|
function consoleHandler(span) {
|
|
9644
10363
|
const duration = span.durationMs !== undefined ? `${span.durationMs}ms` : "running";
|
|
9645
10364
|
const status = span.status === "error" ? "[ERROR]" : span.status === "ok" ? "[OK]" : "[...]";
|
|
9646
|
-
|
|
10365
|
+
log9.info("span", {
|
|
9647
10366
|
trace: span.traceId,
|
|
9648
10367
|
span: span.name,
|
|
9649
10368
|
kind: span.kind,
|
|
@@ -9741,8 +10460,86 @@ function createMetrics(options) {
|
|
|
9741
10460
|
}
|
|
9742
10461
|
};
|
|
9743
10462
|
}
|
|
10463
|
+
// ../observe/src/experiment.ts
|
|
10464
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
10465
|
+
var log10 = createLogger();
|
|
10466
|
+
function createExperiment(config) {
|
|
10467
|
+
const { name, variants } = config;
|
|
10468
|
+
if (variants.length === 0) {
|
|
10469
|
+
throw new Error("Experiment must have at least one variant");
|
|
10470
|
+
}
|
|
10471
|
+
const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
|
|
10472
|
+
const stats = {};
|
|
10473
|
+
for (const v of variants) {
|
|
10474
|
+
stats[v.name] = { assignments: 0, metrics: {} };
|
|
10475
|
+
}
|
|
10476
|
+
function hashAssign(userId) {
|
|
10477
|
+
const hash = createHash2("sha256").update(`${name}:${userId}`).digest();
|
|
10478
|
+
const value = hash.readUInt32BE(0) % 1e4 / 1e4;
|
|
10479
|
+
return pickVariant(value);
|
|
10480
|
+
}
|
|
10481
|
+
function randomAssign() {
|
|
10482
|
+
const value = Math.random();
|
|
10483
|
+
return pickVariant(value);
|
|
10484
|
+
}
|
|
10485
|
+
function pickVariant(value) {
|
|
10486
|
+
let cumulative = 0;
|
|
10487
|
+
for (const v of variants) {
|
|
10488
|
+
cumulative += v.weight / totalWeight;
|
|
10489
|
+
if (value < cumulative)
|
|
10490
|
+
return v;
|
|
10491
|
+
}
|
|
10492
|
+
return variants[variants.length - 1];
|
|
10493
|
+
}
|
|
10494
|
+
return {
|
|
10495
|
+
assign(userId) {
|
|
10496
|
+
const variant = userId ? hashAssign(userId) : randomAssign();
|
|
10497
|
+
const s = stats[variant.name];
|
|
10498
|
+
if (s)
|
|
10499
|
+
s.assignments++;
|
|
10500
|
+
log10.debug("Experiment assignment", {
|
|
10501
|
+
experiment: name,
|
|
10502
|
+
variant: variant.name,
|
|
10503
|
+
userId
|
|
10504
|
+
});
|
|
10505
|
+
return variant;
|
|
10506
|
+
},
|
|
10507
|
+
record(variant, metrics) {
|
|
10508
|
+
const s = stats[variant];
|
|
10509
|
+
if (!s)
|
|
10510
|
+
return;
|
|
10511
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
10512
|
+
if (!s.metrics[key]) {
|
|
10513
|
+
s.metrics[key] = { sum: 0, count: 0 };
|
|
10514
|
+
}
|
|
10515
|
+
s.metrics[key].sum += value;
|
|
10516
|
+
s.metrics[key].count++;
|
|
10517
|
+
}
|
|
10518
|
+
},
|
|
10519
|
+
results() {
|
|
10520
|
+
let totalAssignments = 0;
|
|
10521
|
+
const variantResults = {};
|
|
10522
|
+
for (const [vName, s] of Object.entries(stats)) {
|
|
10523
|
+
totalAssignments += s.assignments;
|
|
10524
|
+
const metricsResult = {};
|
|
10525
|
+
for (const [key, m] of Object.entries(s.metrics)) {
|
|
10526
|
+
metricsResult[key] = {
|
|
10527
|
+
sum: m.sum,
|
|
10528
|
+
count: m.count,
|
|
10529
|
+
avg: m.count > 0 ? m.sum / m.count : 0
|
|
10530
|
+
};
|
|
10531
|
+
}
|
|
10532
|
+
variantResults[vName] = {
|
|
10533
|
+
assignments: s.assignments,
|
|
10534
|
+
metrics: metricsResult
|
|
10535
|
+
};
|
|
10536
|
+
}
|
|
10537
|
+
return { name, totalAssignments, variants: variantResults };
|
|
10538
|
+
}
|
|
10539
|
+
};
|
|
10540
|
+
}
|
|
9744
10541
|
// ../observe/src/otel.ts
|
|
9745
|
-
var
|
|
10542
|
+
var log11 = createLogger();
|
|
9746
10543
|
var SPAN_KIND_MAP = {
|
|
9747
10544
|
llm: 3,
|
|
9748
10545
|
tool: 1,
|
|
@@ -9883,10 +10680,10 @@ function createOTLPExporter(config) {
|
|
|
9883
10680
|
body: JSON.stringify(payload)
|
|
9884
10681
|
});
|
|
9885
10682
|
if (!response.ok) {
|
|
9886
|
-
|
|
10683
|
+
log11.error(`OTLP export failed: ${response.status} ${response.statusText}`);
|
|
9887
10684
|
}
|
|
9888
10685
|
} catch (err2) {
|
|
9889
|
-
|
|
10686
|
+
log11.error("OTLP export error", { error: err2 instanceof Error ? err2.message : String(err2) });
|
|
9890
10687
|
}
|
|
9891
10688
|
}
|
|
9892
10689
|
function startAutoFlush() {
|
|
@@ -12081,12 +12878,12 @@ function requestIdMiddleware() {
|
|
|
12081
12878
|
};
|
|
12082
12879
|
}
|
|
12083
12880
|
function requestLoggerMiddleware(logger) {
|
|
12084
|
-
const
|
|
12881
|
+
const log12 = logger ?? createLogger();
|
|
12085
12882
|
return async (c, next) => {
|
|
12086
12883
|
const start = Date.now();
|
|
12087
12884
|
await next();
|
|
12088
12885
|
const duration = Date.now() - start;
|
|
12089
|
-
|
|
12886
|
+
log12.info(`${c.req.method} ${c.req.path}`, {
|
|
12090
12887
|
method: c.req.method,
|
|
12091
12888
|
path: c.req.path,
|
|
12092
12889
|
status: c.res.status,
|
|
@@ -12096,6 +12893,158 @@ function requestLoggerMiddleware(logger) {
|
|
|
12096
12893
|
};
|
|
12097
12894
|
}
|
|
12098
12895
|
|
|
12896
|
+
// ../../node_modules/.bun/hono@4.12.3/node_modules/hono/dist/utils/stream.js
|
|
12897
|
+
var StreamingApi = class {
|
|
12898
|
+
writer;
|
|
12899
|
+
encoder;
|
|
12900
|
+
writable;
|
|
12901
|
+
abortSubscribers = [];
|
|
12902
|
+
responseReadable;
|
|
12903
|
+
aborted = false;
|
|
12904
|
+
closed = false;
|
|
12905
|
+
constructor(writable, _readable) {
|
|
12906
|
+
this.writable = writable;
|
|
12907
|
+
this.writer = writable.getWriter();
|
|
12908
|
+
this.encoder = new TextEncoder;
|
|
12909
|
+
const reader = _readable.getReader();
|
|
12910
|
+
this.abortSubscribers.push(async () => {
|
|
12911
|
+
await reader.cancel();
|
|
12912
|
+
});
|
|
12913
|
+
this.responseReadable = new ReadableStream({
|
|
12914
|
+
async pull(controller) {
|
|
12915
|
+
const { done, value } = await reader.read();
|
|
12916
|
+
done ? controller.close() : controller.enqueue(value);
|
|
12917
|
+
},
|
|
12918
|
+
cancel: () => {
|
|
12919
|
+
this.abort();
|
|
12920
|
+
}
|
|
12921
|
+
});
|
|
12922
|
+
}
|
|
12923
|
+
async write(input) {
|
|
12924
|
+
try {
|
|
12925
|
+
if (typeof input === "string") {
|
|
12926
|
+
input = this.encoder.encode(input);
|
|
12927
|
+
}
|
|
12928
|
+
await this.writer.write(input);
|
|
12929
|
+
} catch {}
|
|
12930
|
+
return this;
|
|
12931
|
+
}
|
|
12932
|
+
async writeln(input) {
|
|
12933
|
+
await this.write(input + `
|
|
12934
|
+
`);
|
|
12935
|
+
return this;
|
|
12936
|
+
}
|
|
12937
|
+
sleep(ms) {
|
|
12938
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
12939
|
+
}
|
|
12940
|
+
async close() {
|
|
12941
|
+
try {
|
|
12942
|
+
await this.writer.close();
|
|
12943
|
+
} catch {}
|
|
12944
|
+
this.closed = true;
|
|
12945
|
+
}
|
|
12946
|
+
async pipe(body) {
|
|
12947
|
+
this.writer.releaseLock();
|
|
12948
|
+
await body.pipeTo(this.writable, { preventClose: true });
|
|
12949
|
+
this.writer = this.writable.getWriter();
|
|
12950
|
+
}
|
|
12951
|
+
onAbort(listener) {
|
|
12952
|
+
this.abortSubscribers.push(listener);
|
|
12953
|
+
}
|
|
12954
|
+
abort() {
|
|
12955
|
+
if (!this.aborted) {
|
|
12956
|
+
this.aborted = true;
|
|
12957
|
+
this.abortSubscribers.forEach((subscriber) => subscriber());
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
};
|
|
12961
|
+
|
|
12962
|
+
// ../../node_modules/.bun/hono@4.12.3/node_modules/hono/dist/helper/streaming/utils.js
|
|
12963
|
+
var isOldBunVersion = () => {
|
|
12964
|
+
const version = typeof Bun !== "undefined" ? Bun.version : undefined;
|
|
12965
|
+
if (version === undefined) {
|
|
12966
|
+
return false;
|
|
12967
|
+
}
|
|
12968
|
+
const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
|
|
12969
|
+
isOldBunVersion = () => result;
|
|
12970
|
+
return result;
|
|
12971
|
+
};
|
|
12972
|
+
|
|
12973
|
+
// ../../node_modules/.bun/hono@4.12.3/node_modules/hono/dist/helper/streaming/stream.js
|
|
12974
|
+
var contextStash = /* @__PURE__ */ new WeakMap;
|
|
12975
|
+
var stream = (c, cb, onError) => {
|
|
12976
|
+
const { readable, writable } = new TransformStream;
|
|
12977
|
+
const stream2 = new StreamingApi(writable, readable);
|
|
12978
|
+
if (isOldBunVersion()) {
|
|
12979
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
12980
|
+
if (!stream2.closed) {
|
|
12981
|
+
stream2.abort();
|
|
12982
|
+
}
|
|
12983
|
+
});
|
|
12984
|
+
}
|
|
12985
|
+
contextStash.set(stream2.responseReadable, c);
|
|
12986
|
+
(async () => {
|
|
12987
|
+
try {
|
|
12988
|
+
await cb(stream2);
|
|
12989
|
+
} catch (e) {
|
|
12990
|
+
if (e === undefined) {} else if (e instanceof Error && onError) {
|
|
12991
|
+
await onError(e, stream2);
|
|
12992
|
+
} else {
|
|
12993
|
+
console.error(e);
|
|
12994
|
+
}
|
|
12995
|
+
} finally {
|
|
12996
|
+
stream2.close();
|
|
12997
|
+
}
|
|
12998
|
+
})();
|
|
12999
|
+
return c.newResponse(stream2.responseReadable);
|
|
13000
|
+
};
|
|
13001
|
+
|
|
13002
|
+
// ../app/src/sse.ts
|
|
13003
|
+
function sseHeaders() {
|
|
13004
|
+
return {
|
|
13005
|
+
"Content-Type": "text/event-stream",
|
|
13006
|
+
"Cache-Control": "no-cache",
|
|
13007
|
+
Connection: "keep-alive",
|
|
13008
|
+
"X-Accel-Buffering": "no"
|
|
13009
|
+
};
|
|
13010
|
+
}
|
|
13011
|
+
function formatSSE(event, data) {
|
|
13012
|
+
const json = JSON.stringify(data);
|
|
13013
|
+
if (event === "message") {
|
|
13014
|
+
return `data: ${json}
|
|
13015
|
+
|
|
13016
|
+
`;
|
|
13017
|
+
}
|
|
13018
|
+
return `event: ${event}
|
|
13019
|
+
data: ${json}
|
|
13020
|
+
|
|
13021
|
+
`;
|
|
13022
|
+
}
|
|
13023
|
+
function streamResponse(c, source) {
|
|
13024
|
+
const headers = sseHeaders();
|
|
13025
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
13026
|
+
c.header(key, value);
|
|
13027
|
+
}
|
|
13028
|
+
return stream(c, async (s) => {
|
|
13029
|
+
try {
|
|
13030
|
+
for await (const event of source) {
|
|
13031
|
+
const sseData = formatSSE("message", event);
|
|
13032
|
+
await s.write(sseData);
|
|
13033
|
+
}
|
|
13034
|
+
} catch (err2) {
|
|
13035
|
+
const errorEvent = {
|
|
13036
|
+
type: "error",
|
|
13037
|
+
error: err2 instanceof Error ? err2 : new Error(String(err2))
|
|
13038
|
+
};
|
|
13039
|
+
const sseData = formatSSE("error", {
|
|
13040
|
+
type: "error",
|
|
13041
|
+
message: errorEvent.error.message
|
|
13042
|
+
});
|
|
13043
|
+
await s.write(sseData);
|
|
13044
|
+
}
|
|
13045
|
+
});
|
|
13046
|
+
}
|
|
13047
|
+
|
|
12099
13048
|
// ../app/src/routes.ts
|
|
12100
13049
|
function parseJsonBody(raw2) {
|
|
12101
13050
|
try {
|
|
@@ -12168,6 +13117,14 @@ function createRoutes(deps) {
|
|
|
12168
13117
|
if ("error" in resolved) {
|
|
12169
13118
|
return c.json({ error: resolved.error }, 404);
|
|
12170
13119
|
}
|
|
13120
|
+
if (body.stream) {
|
|
13121
|
+
const stream2 = deps.gateway.stream({
|
|
13122
|
+
messages: [{ role: "user", content: body.message }],
|
|
13123
|
+
system: resolved.agent.config.system,
|
|
13124
|
+
model: resolved.agent.config.model
|
|
13125
|
+
});
|
|
13126
|
+
return streamResponse(c, stream2);
|
|
13127
|
+
}
|
|
12171
13128
|
let result;
|
|
12172
13129
|
try {
|
|
12173
13130
|
result = await resolved.agent.run(body.message);
|
|
@@ -12214,6 +13171,16 @@ function createRoutes(deps) {
|
|
|
12214
13171
|
role: m.role,
|
|
12215
13172
|
content: m.content
|
|
12216
13173
|
}));
|
|
13174
|
+
if (body.stream) {
|
|
13175
|
+
const stream2 = deps.gateway.stream({
|
|
13176
|
+
messages,
|
|
13177
|
+
model: body.model,
|
|
13178
|
+
system: body.system,
|
|
13179
|
+
maxTokens: body.maxTokens,
|
|
13180
|
+
temperature: body.temperature
|
|
13181
|
+
});
|
|
13182
|
+
return streamResponse(c, stream2);
|
|
13183
|
+
}
|
|
12217
13184
|
let response;
|
|
12218
13185
|
try {
|
|
12219
13186
|
response = await deps.gateway.complete({
|
|
@@ -12254,13 +13221,13 @@ function createRoutes(deps) {
|
|
|
12254
13221
|
}
|
|
12255
13222
|
|
|
12256
13223
|
// ../app/src/app.ts
|
|
12257
|
-
var
|
|
13224
|
+
var log12 = createLogger();
|
|
12258
13225
|
function createApp(config) {
|
|
12259
13226
|
const app = new Hono2;
|
|
12260
13227
|
app.onError((err2, c) => {
|
|
12261
13228
|
const statusCode = err2 instanceof ElsiumError ? err2.statusCode ?? 500 : 500;
|
|
12262
13229
|
const code = err2 instanceof ElsiumError ? err2.code : "UNKNOWN";
|
|
12263
|
-
|
|
13230
|
+
log12.error("Unhandled error", { error: err2.message, code, path: c.req.path });
|
|
12264
13231
|
return c.json({ error: err2.message, code }, statusCode);
|
|
12265
13232
|
});
|
|
12266
13233
|
app.notFound((c) => {
|
|
@@ -12281,7 +13248,7 @@ function createApp(config) {
|
|
|
12281
13248
|
});
|
|
12282
13249
|
const serverConfig = config.server ?? {};
|
|
12283
13250
|
app.use("*", requestIdMiddleware());
|
|
12284
|
-
app.use("*", requestLoggerMiddleware(
|
|
13251
|
+
app.use("*", requestLoggerMiddleware(log12));
|
|
12285
13252
|
if (serverConfig.cors) {
|
|
12286
13253
|
app.use("*", corsMiddleware(serverConfig.cors));
|
|
12287
13254
|
}
|
|
@@ -12325,11 +13292,11 @@ function createApp(config) {
|
|
|
12325
13292
|
const drainTimeoutMs = typeof serverConfig.gracefulShutdown === "object" ? serverConfig.gracefulShutdown.drainTimeoutMs : undefined;
|
|
12326
13293
|
shutdownManager = createShutdownManager({
|
|
12327
13294
|
drainTimeoutMs,
|
|
12328
|
-
onDrainStart: () =>
|
|
12329
|
-
onDrainComplete: () =>
|
|
13295
|
+
onDrainStart: () => log12.info("Draining connections..."),
|
|
13296
|
+
onDrainComplete: () => log12.info("Drain complete")
|
|
12330
13297
|
});
|
|
12331
13298
|
}
|
|
12332
|
-
|
|
13299
|
+
log12.info("ElsiumAI server started", {
|
|
12333
13300
|
url: `http://${hostname}:${listenPort}`,
|
|
12334
13301
|
routes: ["POST /chat", "POST /complete", "GET /health", "GET /metrics", "GET /agents"]
|
|
12335
13302
|
});
|
|
@@ -12346,7 +13313,54 @@ function createApp(config) {
|
|
|
12346
13313
|
};
|
|
12347
13314
|
}
|
|
12348
13315
|
// ../app/src/rbac.ts
|
|
12349
|
-
var
|
|
13316
|
+
var log13 = createLogger();
|
|
13317
|
+
// ../app/src/tenant.ts
|
|
13318
|
+
var log14 = createLogger();
|
|
13319
|
+
function tenantMiddleware(config) {
|
|
13320
|
+
const { extractTenant, onUnknownTenant = "reject", defaultTenant } = config;
|
|
13321
|
+
return async (c, next) => {
|
|
13322
|
+
const tenant = extractTenant(c);
|
|
13323
|
+
if (!tenant) {
|
|
13324
|
+
if (onUnknownTenant === "default" && defaultTenant) {
|
|
13325
|
+
c.set("tenant", defaultTenant);
|
|
13326
|
+
log14.debug("Using default tenant", { tenantId: defaultTenant.tenantId });
|
|
13327
|
+
} else {
|
|
13328
|
+
return c.json({ error: "Tenant identification required" }, 401);
|
|
13329
|
+
}
|
|
13330
|
+
} else {
|
|
13331
|
+
c.set("tenant", tenant);
|
|
13332
|
+
log14.debug("Tenant identified", { tenantId: tenant.tenantId });
|
|
13333
|
+
}
|
|
13334
|
+
await next();
|
|
13335
|
+
};
|
|
13336
|
+
}
|
|
13337
|
+
function tenantRateLimitMiddleware() {
|
|
13338
|
+
const windows = new Map;
|
|
13339
|
+
return async (c, next) => {
|
|
13340
|
+
const tenant = c.get("tenant");
|
|
13341
|
+
if (!tenant?.limits?.maxRequestsPerMinute) {
|
|
13342
|
+
await next();
|
|
13343
|
+
return;
|
|
13344
|
+
}
|
|
13345
|
+
const limit = tenant.limits.maxRequestsPerMinute;
|
|
13346
|
+
const now = Date.now();
|
|
13347
|
+
const windowMs = 60000;
|
|
13348
|
+
const key = tenant.tenantId;
|
|
13349
|
+
let entry = windows.get(key);
|
|
13350
|
+
if (!entry || now - entry.windowStart > windowMs) {
|
|
13351
|
+
entry = { count: 0, windowStart: now };
|
|
13352
|
+
windows.set(key, entry);
|
|
13353
|
+
}
|
|
13354
|
+
entry.count++;
|
|
13355
|
+
if (entry.count > limit) {
|
|
13356
|
+
return c.json({
|
|
13357
|
+
error: "Rate limit exceeded",
|
|
13358
|
+
retryAfterMs: windowMs - (now - entry.windowStart)
|
|
13359
|
+
}, 429);
|
|
13360
|
+
}
|
|
13361
|
+
await next();
|
|
13362
|
+
};
|
|
13363
|
+
}
|
|
12350
13364
|
// ../mcp/src/client.ts
|
|
12351
13365
|
import { spawn } from "node:child_process";
|
|
12352
13366
|
function createMCPClient(config) {
|
|
@@ -12558,7 +13572,7 @@ function createMCPClient(config) {
|
|
|
12558
13572
|
};
|
|
12559
13573
|
}
|
|
12560
13574
|
// ../mcp/src/server.ts
|
|
12561
|
-
var
|
|
13575
|
+
var log15 = createLogger();
|
|
12562
13576
|
function createMCPServer(config) {
|
|
12563
13577
|
let running = false;
|
|
12564
13578
|
const toolMap = new Map(config.tools.map((t) => [t.name, t]));
|
|
@@ -12693,7 +13707,7 @@ function createMCPServer(config) {
|
|
|
12693
13707
|
pendingChunks.shift();
|
|
12694
13708
|
buffer += chunk;
|
|
12695
13709
|
if (buffer.length > MAX_BUFFER_SIZE2) {
|
|
12696
|
-
|
|
13710
|
+
log15.error("MCP server: buffer size limit exceeded, resetting");
|
|
12697
13711
|
buffer = "";
|
|
12698
13712
|
continue;
|
|
12699
13713
|
}
|
|
@@ -12728,6 +13742,119 @@ function createMCPServer(config) {
|
|
|
12728
13742
|
}
|
|
12729
13743
|
};
|
|
12730
13744
|
}
|
|
13745
|
+
// ../client/dist/index.js
|
|
13746
|
+
async function* parseSSEStream(response) {
|
|
13747
|
+
if (!response.body) {
|
|
13748
|
+
throw new Error("Response body is null");
|
|
13749
|
+
}
|
|
13750
|
+
const reader = response.body.getReader();
|
|
13751
|
+
const decoder = new TextDecoder;
|
|
13752
|
+
let buffer = "";
|
|
13753
|
+
try {
|
|
13754
|
+
while (true) {
|
|
13755
|
+
const { done, value } = await reader.read();
|
|
13756
|
+
if (done)
|
|
13757
|
+
break;
|
|
13758
|
+
buffer += decoder.decode(value, { stream: true });
|
|
13759
|
+
const lines = buffer.split(`
|
|
13760
|
+
`);
|
|
13761
|
+
buffer = lines.pop() ?? "";
|
|
13762
|
+
for (const line of lines) {
|
|
13763
|
+
if (line.startsWith("event: error")) {
|
|
13764
|
+
continue;
|
|
13765
|
+
}
|
|
13766
|
+
if (!line.startsWith("data: "))
|
|
13767
|
+
continue;
|
|
13768
|
+
const data = line.slice(6).trim();
|
|
13769
|
+
if (!data || data === "[DONE]")
|
|
13770
|
+
continue;
|
|
13771
|
+
try {
|
|
13772
|
+
const event = JSON.parse(data);
|
|
13773
|
+
yield event;
|
|
13774
|
+
} catch {}
|
|
13775
|
+
}
|
|
13776
|
+
}
|
|
13777
|
+
} finally {
|
|
13778
|
+
reader.releaseLock();
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13781
|
+
function createClient(config) {
|
|
13782
|
+
const { baseUrl, apiKey, timeout = 30000 } = config;
|
|
13783
|
+
function headers() {
|
|
13784
|
+
const h = {
|
|
13785
|
+
"Content-Type": "application/json"
|
|
13786
|
+
};
|
|
13787
|
+
if (apiKey) {
|
|
13788
|
+
h.Authorization = `Bearer ${apiKey}`;
|
|
13789
|
+
}
|
|
13790
|
+
return h;
|
|
13791
|
+
}
|
|
13792
|
+
async function request(method, path, body) {
|
|
13793
|
+
const controller = new AbortController;
|
|
13794
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
13795
|
+
try {
|
|
13796
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
13797
|
+
method,
|
|
13798
|
+
headers: headers(),
|
|
13799
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
13800
|
+
signal: controller.signal
|
|
13801
|
+
});
|
|
13802
|
+
if (!response.ok) {
|
|
13803
|
+
const errorBody = await response.text().catch(() => "Unknown error");
|
|
13804
|
+
throw new Error(`HTTP ${response.status}: ${errorBody}`);
|
|
13805
|
+
}
|
|
13806
|
+
return await response.json();
|
|
13807
|
+
} finally {
|
|
13808
|
+
clearTimeout(timer);
|
|
13809
|
+
}
|
|
13810
|
+
}
|
|
13811
|
+
async function streamRequest(path, body) {
|
|
13812
|
+
const controller = new AbortController;
|
|
13813
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
13814
|
+
try {
|
|
13815
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
13816
|
+
method: "POST",
|
|
13817
|
+
headers: headers(),
|
|
13818
|
+
body: JSON.stringify(body),
|
|
13819
|
+
signal: controller.signal
|
|
13820
|
+
});
|
|
13821
|
+
if (!response.ok) {
|
|
13822
|
+
const errorBody = await response.text().catch(() => "Unknown error");
|
|
13823
|
+
clearTimeout(timer);
|
|
13824
|
+
throw new Error(`HTTP ${response.status}: ${errorBody}`);
|
|
13825
|
+
}
|
|
13826
|
+
return response;
|
|
13827
|
+
} catch (err2) {
|
|
13828
|
+
clearTimeout(timer);
|
|
13829
|
+
throw err2;
|
|
13830
|
+
}
|
|
13831
|
+
}
|
|
13832
|
+
return {
|
|
13833
|
+
async chat(req) {
|
|
13834
|
+
return request("POST", "/chat", { ...req, stream: false });
|
|
13835
|
+
},
|
|
13836
|
+
async* chatStream(req) {
|
|
13837
|
+
const response = await streamRequest("/chat", { ...req, stream: true });
|
|
13838
|
+
yield* parseSSEStream(response);
|
|
13839
|
+
},
|
|
13840
|
+
async complete(req) {
|
|
13841
|
+
return request("POST", "/complete", { ...req, stream: false });
|
|
13842
|
+
},
|
|
13843
|
+
async* completeStream(req) {
|
|
13844
|
+
const response = await streamRequest("/complete", { ...req, stream: true });
|
|
13845
|
+
yield* parseSSEStream(response);
|
|
13846
|
+
},
|
|
13847
|
+
async health() {
|
|
13848
|
+
return request("GET", "/health");
|
|
13849
|
+
},
|
|
13850
|
+
async metrics() {
|
|
13851
|
+
return request("GET", "/metrics");
|
|
13852
|
+
},
|
|
13853
|
+
async agents() {
|
|
13854
|
+
return request("GET", "/agents");
|
|
13855
|
+
}
|
|
13856
|
+
};
|
|
13857
|
+
}
|
|
12731
13858
|
// ../testing/src/mock-provider.ts
|
|
12732
13859
|
function mockProvider(options = {}) {
|
|
12733
13860
|
const { responses = [], defaultResponse, onRequest } = options;
|
|
@@ -12836,10 +13963,10 @@ function mockProvider(options = {}) {
|
|
|
12836
13963
|
};
|
|
12837
13964
|
}
|
|
12838
13965
|
// ../testing/src/fixtures.ts
|
|
12839
|
-
import { createHash } from "node:crypto";
|
|
13966
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
12840
13967
|
function hashMessages(messages) {
|
|
12841
13968
|
const content = messages.map((m) => `${m.role}:${m.content}`).join("|");
|
|
12842
|
-
return
|
|
13969
|
+
return createHash3("sha256").update(content).digest("hex").slice(0, 16);
|
|
12843
13970
|
}
|
|
12844
13971
|
function createFixture(name, entries) {
|
|
12845
13972
|
return {
|
|
@@ -13505,7 +14632,9 @@ function createReplayPlayer(entriesOrJson) {
|
|
|
13505
14632
|
};
|
|
13506
14633
|
}
|
|
13507
14634
|
export {
|
|
14635
|
+
zodToJsonSchema,
|
|
13508
14636
|
xrayMiddleware,
|
|
14637
|
+
vectorStoreRegistry,
|
|
13509
14638
|
unwrapOr,
|
|
13510
14639
|
unwrap,
|
|
13511
14640
|
tryCatchSync,
|
|
@@ -13513,7 +14642,11 @@ export {
|
|
|
13513
14642
|
toTraceparent,
|
|
13514
14643
|
toOTelSpan,
|
|
13515
14644
|
toOTelExportRequest,
|
|
14645
|
+
tenantRateLimitMiddleware,
|
|
14646
|
+
tenantMiddleware,
|
|
14647
|
+
streamResponse,
|
|
13516
14648
|
step,
|
|
14649
|
+
sseHeaders,
|
|
13517
14650
|
sleep,
|
|
13518
14651
|
securityMiddleware,
|
|
13519
14652
|
runSupervisor,
|
|
@@ -13526,6 +14659,7 @@ export {
|
|
|
13526
14659
|
redactSecrets,
|
|
13527
14660
|
rag,
|
|
13528
14661
|
parseTraceparent,
|
|
14662
|
+
outputGuardrailMiddleware,
|
|
13529
14663
|
ok,
|
|
13530
14664
|
observe,
|
|
13531
14665
|
mockProvider,
|
|
@@ -13541,6 +14675,7 @@ export {
|
|
|
13541
14675
|
gateway,
|
|
13542
14676
|
formatToolResultAsText,
|
|
13543
14677
|
formatToolResult,
|
|
14678
|
+
formatSSE,
|
|
13544
14679
|
formatEvalReport,
|
|
13545
14680
|
extractTraceContext,
|
|
13546
14681
|
extractText,
|
|
@@ -13549,6 +14684,7 @@ export {
|
|
|
13549
14684
|
envNumber,
|
|
13550
14685
|
envBool,
|
|
13551
14686
|
env,
|
|
14687
|
+
embeddingProviderRegistry,
|
|
13552
14688
|
detectPromptInjection,
|
|
13553
14689
|
detectJailbreak,
|
|
13554
14690
|
defineWorkflow,
|
|
@@ -13560,15 +14696,18 @@ export {
|
|
|
13560
14696
|
currentTimeTool,
|
|
13561
14697
|
createToolkit,
|
|
13562
14698
|
createStream,
|
|
14699
|
+
createSqliteMemoryStore,
|
|
13563
14700
|
createSpan,
|
|
13564
14701
|
createSnapshotStore,
|
|
13565
14702
|
createSemanticValidator,
|
|
13566
14703
|
createReplayRecorder,
|
|
13567
14704
|
createReplayPlayer,
|
|
13568
14705
|
createRegressionSuite,
|
|
14706
|
+
createRegistry,
|
|
13569
14707
|
createRecorder,
|
|
13570
14708
|
createProviderMesh,
|
|
13571
14709
|
createPromptRegistry,
|
|
14710
|
+
createPgVectorStore,
|
|
13572
14711
|
createOpenAIProvider,
|
|
13573
14712
|
createOpenAIEmbeddings,
|
|
13574
14713
|
createOTLPExporter,
|
|
@@ -13579,17 +14718,25 @@ export {
|
|
|
13579
14718
|
createMCPClient,
|
|
13580
14719
|
createLogger,
|
|
13581
14720
|
createInMemoryStore,
|
|
14721
|
+
createInMemoryMemoryStore,
|
|
14722
|
+
createInMemoryCache,
|
|
13582
14723
|
createGoogleProvider,
|
|
13583
14724
|
createFixture,
|
|
14725
|
+
createExperiment,
|
|
13584
14726
|
createCostEngine,
|
|
14727
|
+
createContextManager,
|
|
13585
14728
|
createConfidenceScorer,
|
|
14729
|
+
createClient,
|
|
14730
|
+
createBatch,
|
|
13586
14731
|
createApp,
|
|
13587
14732
|
createAnthropicProvider,
|
|
13588
14733
|
createAgentSecurity,
|
|
14734
|
+
countTokens,
|
|
13589
14735
|
costTrackingMiddleware,
|
|
13590
14736
|
composeMiddleware,
|
|
13591
14737
|
checkBlockedPatterns,
|
|
13592
14738
|
calculatorTool,
|
|
13593
14739
|
calculateCost,
|
|
14740
|
+
cacheMiddleware,
|
|
13594
14741
|
ElsiumError
|
|
13595
14742
|
};
|