avantgate 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/errors.d.ts +14 -0
- package/dist/agent/errors.d.ts.map +1 -1
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +317 -49
- package/dist/agent/index.mjs +51 -44
- package/dist/agent/isolated-tool.d.ts.map +1 -1
- package/dist/agent/registry.d.ts +2 -15
- package/dist/agent/registry.d.ts.map +1 -1
- package/dist/agent/strategy.d.ts.map +1 -1
- package/dist/agent/tool-resilience.d.ts +5 -0
- package/dist/agent/tool-resilience.d.ts.map +1 -0
- package/dist/agent/types.d.ts +66 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/{chunk-W27A25DK.mjs → chunk-B22KKIQQ.mjs} +271 -6
- package/dist/{chunk-AH5XXSS4.mjs → chunk-KEJ4VNSN.mjs} +1 -1
- package/dist/control-layer.d.ts +11 -2
- package/dist/control-layer.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +723 -106
- package/dist/index.mjs +452 -102
- package/dist/middleware.d.ts +4 -4
- package/dist/middleware.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/providers/http-client.d.ts +3 -0
- package/dist/providers/http-client.d.ts.map +1 -1
- package/dist/types.d.ts +53 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/immutability.d.ts +5 -0
- package/dist/utils/immutability.d.ts.map +1 -0
- package/dist/workflow/index.js +264 -6
- package/dist/workflow/index.mjs +2 -2
- package/package.json +5 -4
package/dist/index.mjs
CHANGED
|
@@ -9,15 +9,22 @@ import {
|
|
|
9
9
|
defineWorkflow,
|
|
10
10
|
executeWorkflow,
|
|
11
11
|
isWorkflowAbortSignal
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KEJ4VNSN.mjs";
|
|
13
13
|
import {
|
|
14
14
|
sanitizePII
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-B22KKIQQ.mjs";
|
|
16
16
|
import {
|
|
17
17
|
__require
|
|
18
18
|
} from "./chunk-Y6FXYEAI.mjs";
|
|
19
19
|
|
|
20
20
|
// src/types.ts
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
var ChatRoleSchema = z.enum(["system", "user", "assistant", "tool"]);
|
|
23
|
+
var ChatMessageSchema = z.object({
|
|
24
|
+
role: ChatRoleSchema,
|
|
25
|
+
content: z.string().min(1, "Message content cannot be empty")
|
|
26
|
+
}).strict();
|
|
27
|
+
var ChatMessagesArraySchema = z.array(ChatMessageSchema);
|
|
21
28
|
var ConfigurationError = class extends Error {
|
|
22
29
|
constructor(message) {
|
|
23
30
|
super(message);
|
|
@@ -30,6 +37,22 @@ var BudgetExceededError = class extends Error {
|
|
|
30
37
|
this.name = "BudgetExceededError";
|
|
31
38
|
}
|
|
32
39
|
};
|
|
40
|
+
var ProviderTTFTTimeoutError = class extends Error {
|
|
41
|
+
ttftTimeoutMs;
|
|
42
|
+
constructor(message, ttftTimeoutMs) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "ProviderTTFTTimeoutError";
|
|
45
|
+
this.ttftTimeoutMs = ttftTimeoutMs;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var ProviderIdleTimeoutError = class extends Error {
|
|
49
|
+
idleTimeoutMs;
|
|
50
|
+
constructor(message, idleTimeoutMs) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "ProviderIdleTimeoutError";
|
|
53
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
33
56
|
var SecretLeakBlockedError = class extends Error {
|
|
34
57
|
detections;
|
|
35
58
|
constructor(message, detections) {
|
|
@@ -488,11 +511,15 @@ var HttpProviderClient = class {
|
|
|
488
511
|
baseUrl;
|
|
489
512
|
apiKey;
|
|
490
513
|
providerType;
|
|
514
|
+
ttftTimeoutMs;
|
|
515
|
+
idleTimeoutMs;
|
|
491
516
|
constructor(config) {
|
|
492
517
|
this.providerType = config.provider;
|
|
493
518
|
this.name = `${config.provider}-http`;
|
|
494
519
|
this.apiKey = config.apiKey;
|
|
495
520
|
this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
|
|
521
|
+
this.ttftTimeoutMs = config.ttftTimeoutMs ?? 15e3;
|
|
522
|
+
this.idleTimeoutMs = config.idleTimeoutMs ?? 5e3;
|
|
496
523
|
}
|
|
497
524
|
resolveEndpoint = () => {
|
|
498
525
|
const trimmed = this.baseUrl.replace(/\/+$/, "");
|
|
@@ -514,7 +541,7 @@ var HttpProviderClient = class {
|
|
|
514
541
|
}
|
|
515
542
|
return headers;
|
|
516
543
|
};
|
|
517
|
-
buildPayload = (options, model) => {
|
|
544
|
+
buildPayload = (options, model, stream = false) => {
|
|
518
545
|
return {
|
|
519
546
|
model,
|
|
520
547
|
messages: options.messages.map((m) => ({
|
|
@@ -522,6 +549,7 @@ var HttpProviderClient = class {
|
|
|
522
549
|
content: m.content
|
|
523
550
|
})),
|
|
524
551
|
temperature: options.temperature ?? 0.2,
|
|
552
|
+
stream,
|
|
525
553
|
...options.responseFormat ? { response_format: options.responseFormat } : {}
|
|
526
554
|
};
|
|
527
555
|
};
|
|
@@ -536,22 +564,101 @@ var HttpProviderClient = class {
|
|
|
536
564
|
promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
|
|
537
565
|
};
|
|
538
566
|
};
|
|
567
|
+
readStreamWithHeartbeat = async (reader, controller) => {
|
|
568
|
+
const decoder = new TextDecoder();
|
|
569
|
+
let accumulatedText = "";
|
|
570
|
+
let isFirstTokenReceived = false;
|
|
571
|
+
let activeTimer = setTimeout(() => {
|
|
572
|
+
controller.abort();
|
|
573
|
+
}, this.ttftTimeoutMs);
|
|
574
|
+
try {
|
|
575
|
+
while (true) {
|
|
576
|
+
const { done, value } = await reader.read();
|
|
577
|
+
if (controller.signal.aborted) {
|
|
578
|
+
throw new Error("Aborted");
|
|
579
|
+
}
|
|
580
|
+
if (done) {
|
|
581
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
if (value && value.length > 0) {
|
|
585
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
586
|
+
if (!isFirstTokenReceived) {
|
|
587
|
+
isFirstTokenReceived = true;
|
|
588
|
+
}
|
|
589
|
+
activeTimer = setTimeout(() => {
|
|
590
|
+
controller.abort();
|
|
591
|
+
reader.cancel().catch(() => {
|
|
592
|
+
});
|
|
593
|
+
}, this.idleTimeoutMs);
|
|
594
|
+
accumulatedText += decoder.decode(value, { stream: true });
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
} catch (err) {
|
|
598
|
+
if (controller.signal.aborted) {
|
|
599
|
+
if (!isFirstTokenReceived) {
|
|
600
|
+
throw new ProviderTTFTTimeoutError(
|
|
601
|
+
`[AvantGate HTTP Provider ${this.providerType}] Time-to-First-Token (TTFT) timeout of ${this.ttftTimeoutMs}ms exceeded without receiving initial response bytes.`,
|
|
602
|
+
this.ttftTimeoutMs
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
throw new ProviderIdleTimeoutError(
|
|
606
|
+
`[AvantGate HTTP Provider ${this.providerType}] Idle read timeout of ${this.idleTimeoutMs}ms exceeded during stream generation.`,
|
|
607
|
+
this.idleTimeoutMs
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
throw err;
|
|
611
|
+
} finally {
|
|
612
|
+
if (activeTimer) clearTimeout(activeTimer);
|
|
613
|
+
}
|
|
614
|
+
return accumulatedText;
|
|
615
|
+
};
|
|
539
616
|
complete = async (options) => {
|
|
540
617
|
const endpoint = this.resolveEndpoint();
|
|
541
618
|
const model = options.model ?? "default";
|
|
542
619
|
const headers = this.buildHeaders();
|
|
543
620
|
const body = JSON.stringify(this.buildPayload(options, model));
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
621
|
+
const abortController = new AbortController();
|
|
622
|
+
const ttftTimer = setTimeout(() => {
|
|
623
|
+
abortController.abort();
|
|
624
|
+
}, this.ttftTimeoutMs);
|
|
625
|
+
let response;
|
|
626
|
+
try {
|
|
627
|
+
response = await fetch(endpoint, {
|
|
628
|
+
method: "POST",
|
|
629
|
+
headers,
|
|
630
|
+
body,
|
|
631
|
+
signal: abortController.signal
|
|
632
|
+
});
|
|
633
|
+
} catch (err) {
|
|
634
|
+
if (abortController.signal.aborted) {
|
|
635
|
+
throw new ProviderTTFTTimeoutError(
|
|
636
|
+
`[AvantGate HTTP Provider ${this.providerType}] Time-to-First-Token (TTFT) timeout of ${this.ttftTimeoutMs}ms exceeded before receiving headers.`,
|
|
637
|
+
this.ttftTimeoutMs
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
throw err;
|
|
641
|
+
} finally {
|
|
642
|
+
clearTimeout(ttftTimer);
|
|
643
|
+
}
|
|
549
644
|
if (!response.ok) {
|
|
550
645
|
const errorText = await response.text();
|
|
551
646
|
throw new Error(
|
|
552
647
|
`[AvantGate HTTP Provider ${this.providerType}] HTTP ${response.status} ${response.statusText}: ${errorText}`
|
|
553
648
|
);
|
|
554
649
|
}
|
|
650
|
+
if (response.body) {
|
|
651
|
+
const reader = response.body.getReader();
|
|
652
|
+
const rawText = await this.readStreamWithHeartbeat(reader, abortController);
|
|
653
|
+
try {
|
|
654
|
+
const data2 = JSON.parse(rawText);
|
|
655
|
+
const text2 = data2.choices?.[0]?.message?.content ?? "";
|
|
656
|
+
const usage2 = this.extractUsage(data2.usage);
|
|
657
|
+
return { text: text2, usage: usage2 };
|
|
658
|
+
} catch {
|
|
659
|
+
return { text: rawText };
|
|
660
|
+
}
|
|
661
|
+
}
|
|
555
662
|
const data = await response.json();
|
|
556
663
|
const text = data.choices?.[0]?.message?.content ?? "";
|
|
557
664
|
const usage = this.extractUsage(data.usage);
|
|
@@ -736,23 +843,110 @@ var applyOutputGuards = (rawOutput, options) => {
|
|
|
736
843
|
};
|
|
737
844
|
};
|
|
738
845
|
|
|
846
|
+
// src/utils/immutability.ts
|
|
847
|
+
var deepFreeze = (target, options, currentDepth = 0, visited = /* @__PURE__ */ new WeakSet()) => {
|
|
848
|
+
if (target === null || typeof target !== "object") {
|
|
849
|
+
return target;
|
|
850
|
+
}
|
|
851
|
+
if (visited.has(target)) {
|
|
852
|
+
return target;
|
|
853
|
+
}
|
|
854
|
+
const maxDepth = options?.maxDepth ?? 10;
|
|
855
|
+
if (Object.isFrozen(target) || currentDepth >= maxDepth) {
|
|
856
|
+
return target;
|
|
857
|
+
}
|
|
858
|
+
if (target instanceof Date || target instanceof RegExp || target instanceof Promise) {
|
|
859
|
+
return Object.freeze(target);
|
|
860
|
+
}
|
|
861
|
+
visited.add(target);
|
|
862
|
+
const values = Array.isArray(target) ? target : Object.values(target);
|
|
863
|
+
for (const value of values) {
|
|
864
|
+
if (value && typeof value === "object") {
|
|
865
|
+
deepFreeze(value, options, currentDepth + 1, visited);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return Object.freeze(target);
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// src/errors.ts
|
|
872
|
+
var MiddlewareTimeoutError = class extends Error {
|
|
873
|
+
middlewareName;
|
|
874
|
+
timeoutMs;
|
|
875
|
+
phase;
|
|
876
|
+
constructor(middlewareName, phase, timeoutMs) {
|
|
877
|
+
super(`Middleware "${middlewareName}" execution timed out during ${phase} after ${timeoutMs}ms.`);
|
|
878
|
+
this.name = "MiddlewareTimeoutError";
|
|
879
|
+
this.middlewareName = middlewareName;
|
|
880
|
+
this.phase = phase;
|
|
881
|
+
this.timeoutMs = timeoutMs;
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
var InvalidMessageSchemaError = class extends Error {
|
|
885
|
+
middlewareName;
|
|
886
|
+
validationErrors;
|
|
887
|
+
constructor(middlewareName, validationErrors) {
|
|
888
|
+
super(`Middleware "${middlewareName}" returned an invalid messages payload failing ChatMessageSchema validation.`);
|
|
889
|
+
this.name = "InvalidMessageSchemaError";
|
|
890
|
+
this.middlewareName = middlewareName;
|
|
891
|
+
this.validationErrors = validationErrors;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
var PromptInjectionError = class extends Error {
|
|
895
|
+
reason;
|
|
896
|
+
constructor(reason) {
|
|
897
|
+
super(`[AvantGate Security Guard] Request blocked: ${reason}`);
|
|
898
|
+
this.name = "PromptInjectionError";
|
|
899
|
+
this.reason = reason;
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
|
|
739
903
|
// src/middleware.ts
|
|
740
|
-
var
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
metadata: current.metadata
|
|
904
|
+
var executeWithTimeout = async (action, timeoutMs, middlewareName, phase) => {
|
|
905
|
+
let timer;
|
|
906
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
907
|
+
timer = setTimeout(() => {
|
|
908
|
+
reject(new MiddlewareTimeoutError(middlewareName, phase, timeoutMs));
|
|
909
|
+
}, timeoutMs);
|
|
747
910
|
});
|
|
911
|
+
try {
|
|
912
|
+
return await Promise.race([Promise.resolve(action()), timeoutPromise]);
|
|
913
|
+
} finally {
|
|
914
|
+
if (timer) {
|
|
915
|
+
clearTimeout(timer);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
var validateReturnedMessages = (messages, middlewareName) => {
|
|
920
|
+
if (!messages) return;
|
|
921
|
+
const parseResult = ChatMessagesArraySchema.safeParse(messages);
|
|
922
|
+
if (!parseResult.success) {
|
|
923
|
+
throw new InvalidMessageSchemaError(middlewareName, parseResult.error.format());
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
var applySingleBeforeMiddleware = async (middleware, current, model, temperature, timeoutMs = 5e3) => {
|
|
927
|
+
if (!middleware.beforeRequest) return current;
|
|
928
|
+
const frozenMessages = deepFreeze([...current.messages]);
|
|
929
|
+
const frozenMetadata = current.metadata ? deepFreeze({ ...current.metadata }) : void 0;
|
|
930
|
+
const result = await executeWithTimeout(
|
|
931
|
+
() => middleware.beforeRequest({
|
|
932
|
+
messages: frozenMessages,
|
|
933
|
+
model,
|
|
934
|
+
temperature,
|
|
935
|
+
metadata: frozenMetadata
|
|
936
|
+
}),
|
|
937
|
+
timeoutMs,
|
|
938
|
+
middleware.name,
|
|
939
|
+
"beforeRequest"
|
|
940
|
+
);
|
|
748
941
|
if (!result) return current;
|
|
942
|
+
validateReturnedMessages(result.messages, middleware.name);
|
|
749
943
|
return {
|
|
750
944
|
messages: result.messages ?? current.messages,
|
|
751
945
|
shortCircuit: result.shortCircuit,
|
|
752
946
|
metadata: current.metadata
|
|
753
947
|
};
|
|
754
948
|
};
|
|
755
|
-
var executeBeforeRequestHooks = async (middlewares, context) => {
|
|
949
|
+
var executeBeforeRequestHooks = async (middlewares, context, timeoutMs = 5e3) => {
|
|
756
950
|
if (!middlewares || middlewares.length === 0) {
|
|
757
951
|
return { messages: context.messages, metadata: context.metadata };
|
|
758
952
|
}
|
|
@@ -761,25 +955,37 @@ var executeBeforeRequestHooks = async (middlewares, context) => {
|
|
|
761
955
|
metadata: context.metadata
|
|
762
956
|
};
|
|
763
957
|
for (const middleware of middlewares) {
|
|
764
|
-
current = await applySingleBeforeMiddleware(
|
|
958
|
+
current = await applySingleBeforeMiddleware(
|
|
959
|
+
middleware,
|
|
960
|
+
current,
|
|
961
|
+
context.model,
|
|
962
|
+
context.temperature,
|
|
963
|
+
timeoutMs
|
|
964
|
+
);
|
|
765
965
|
if (current.shortCircuit) return current;
|
|
766
966
|
}
|
|
767
967
|
return current;
|
|
768
968
|
};
|
|
769
|
-
var applySingleAfterMiddleware = async (middleware, current, baseContext) => {
|
|
969
|
+
var applySingleAfterMiddleware = async (middleware, current, baseContext, timeoutMs = 5e3) => {
|
|
770
970
|
if (!middleware.afterResponse) return current;
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
971
|
+
const frozenMetadata = current.metadata ? deepFreeze({ ...current.metadata }) : void 0;
|
|
972
|
+
const result = await executeWithTimeout(
|
|
973
|
+
() => middleware.afterResponse({
|
|
974
|
+
...baseContext,
|
|
975
|
+
responseText: current.responseText,
|
|
976
|
+
metadata: frozenMetadata
|
|
977
|
+
}),
|
|
978
|
+
timeoutMs,
|
|
979
|
+
middleware.name,
|
|
980
|
+
"afterResponse"
|
|
981
|
+
);
|
|
776
982
|
if (!result) return current;
|
|
777
983
|
return {
|
|
778
984
|
responseText: result.responseText ?? current.responseText,
|
|
779
985
|
metadata: result.metadata ?? current.metadata
|
|
780
986
|
};
|
|
781
987
|
};
|
|
782
|
-
var executeAfterResponseHooks = async (middlewares, context) => {
|
|
988
|
+
var executeAfterResponseHooks = async (middlewares, context, timeoutMs = 5e3) => {
|
|
783
989
|
if (!middlewares || middlewares.length === 0) {
|
|
784
990
|
return { responseText: context.responseText, metadata: context.metadata };
|
|
785
991
|
}
|
|
@@ -788,7 +994,7 @@ var executeAfterResponseHooks = async (middlewares, context) => {
|
|
|
788
994
|
metadata: context.metadata
|
|
789
995
|
};
|
|
790
996
|
for (const middleware of middlewares) {
|
|
791
|
-
current = await applySingleAfterMiddleware(middleware, current, context);
|
|
997
|
+
current = await applySingleAfterMiddleware(middleware, current, context, timeoutMs);
|
|
792
998
|
}
|
|
793
999
|
return current;
|
|
794
1000
|
};
|
|
@@ -797,6 +1003,8 @@ var executeAfterResponseHooks = async (middlewares, context) => {
|
|
|
797
1003
|
var AvantGateControlLayer = class {
|
|
798
1004
|
config;
|
|
799
1005
|
cachedPricingAdapter;
|
|
1006
|
+
reservations = /* @__PURE__ */ new Map();
|
|
1007
|
+
cachedProviderChain;
|
|
800
1008
|
constructor(config) {
|
|
801
1009
|
this.config = {
|
|
802
1010
|
...config,
|
|
@@ -804,6 +1012,11 @@ var AvantGateControlLayer = class {
|
|
|
804
1012
|
fallback: this.resolveProviderConfig(config.fallback),
|
|
805
1013
|
emergencyFallback: this.resolveProviderConfig(config.emergencyFallback)
|
|
806
1014
|
};
|
|
1015
|
+
this.cachedProviderChain = [
|
|
1016
|
+
this.config.primary,
|
|
1017
|
+
this.config.fallback,
|
|
1018
|
+
this.config.emergencyFallback
|
|
1019
|
+
].filter((provider) => Boolean(provider?.client));
|
|
807
1020
|
if (config.pricingAdapter) {
|
|
808
1021
|
this.cachedPricingAdapter = new CachedPricingAdapter(
|
|
809
1022
|
config.pricingAdapter,
|
|
@@ -811,6 +1024,12 @@ var AvantGateControlLayer = class {
|
|
|
811
1024
|
);
|
|
812
1025
|
}
|
|
813
1026
|
}
|
|
1027
|
+
getReservation = (id) => {
|
|
1028
|
+
return this.reservations.get(id);
|
|
1029
|
+
};
|
|
1030
|
+
getAllReservations = () => {
|
|
1031
|
+
return Array.from(this.reservations.values());
|
|
1032
|
+
};
|
|
814
1033
|
resolveProviderConfig = (provider) => {
|
|
815
1034
|
if (!provider) return void 0;
|
|
816
1035
|
if (provider.client) return provider;
|
|
@@ -823,16 +1042,15 @@ var AvantGateControlLayer = class {
|
|
|
823
1042
|
return provider;
|
|
824
1043
|
};
|
|
825
1044
|
findProviderConfig = (provider, model) => {
|
|
826
|
-
const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
|
|
827
|
-
(p) => Boolean(p)
|
|
828
|
-
);
|
|
829
1045
|
if (provider) {
|
|
830
|
-
|
|
831
|
-
if (
|
|
1046
|
+
if (this.config.primary.provider === provider) return this.config.primary;
|
|
1047
|
+
if (this.config.fallback?.provider === provider) return this.config.fallback;
|
|
1048
|
+
if (this.config.emergencyFallback?.provider === provider) return this.config.emergencyFallback;
|
|
832
1049
|
}
|
|
833
1050
|
if (model) {
|
|
834
|
-
|
|
835
|
-
if (
|
|
1051
|
+
if (this.config.primary.model === model) return this.config.primary;
|
|
1052
|
+
if (this.config.fallback?.model === model) return this.config.fallback;
|
|
1053
|
+
if (this.config.emergencyFallback?.model === model) return this.config.emergencyFallback;
|
|
836
1054
|
}
|
|
837
1055
|
return this.config.primary;
|
|
838
1056
|
};
|
|
@@ -851,7 +1069,7 @@ var AvantGateControlLayer = class {
|
|
|
851
1069
|
maxLength: this.config.security?.maxInputLength
|
|
852
1070
|
});
|
|
853
1071
|
if (!guard.valid) {
|
|
854
|
-
throw new
|
|
1072
|
+
throw new PromptInjectionError(guard.blockedReason ?? "Prompt injection detected");
|
|
855
1073
|
}
|
|
856
1074
|
let processed = userQuery;
|
|
857
1075
|
if (this.config.security?.customRedactionTerms && this.config.security.customRedactionTerms.length > 0) {
|
|
@@ -862,6 +1080,21 @@ var AvantGateControlLayer = class {
|
|
|
862
1080
|
}
|
|
863
1081
|
return processed;
|
|
864
1082
|
};
|
|
1083
|
+
reScanMessagesAfterHooks = (originalMessages, updatedMessages) => {
|
|
1084
|
+
if (this.config.reScanIngressAfterHooks === false || originalMessages === updatedMessages) {
|
|
1085
|
+
return updatedMessages;
|
|
1086
|
+
}
|
|
1087
|
+
return updatedMessages.map((msg, idx) => {
|
|
1088
|
+
const orig = originalMessages[idx];
|
|
1089
|
+
if (orig && (orig === msg || orig.role === msg.role && orig.content === msg.content)) {
|
|
1090
|
+
return msg;
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
...msg,
|
|
1094
|
+
content: this.applySecurityGuards(msg.content)
|
|
1095
|
+
};
|
|
1096
|
+
});
|
|
1097
|
+
};
|
|
865
1098
|
applyOutputSecurityGuards = (rawOutput) => {
|
|
866
1099
|
const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
|
|
867
1100
|
const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
|
|
@@ -877,33 +1110,79 @@ var AvantGateControlLayer = class {
|
|
|
877
1110
|
});
|
|
878
1111
|
return guardResult.text;
|
|
879
1112
|
};
|
|
880
|
-
|
|
881
|
-
if (this.config.maxTokenBudget !== void 0 &&
|
|
1113
|
+
validateTokenBudget = (estimatedTokens) => {
|
|
1114
|
+
if (this.config.maxTokenBudget !== void 0 && estimatedTokens > this.config.maxTokenBudget) {
|
|
882
1115
|
throw new BudgetExceededError(
|
|
883
|
-
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${
|
|
1116
|
+
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
884
1117
|
);
|
|
885
1118
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1119
|
+
};
|
|
1120
|
+
validatePricingConfigured = async (targetModel, provider) => {
|
|
1121
|
+
const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
|
|
1122
|
+
if (isLocalFree) return;
|
|
1123
|
+
const providerCfg = this.findProviderConfig(provider, targetModel);
|
|
1124
|
+
const price = await resolveModelPriceAsync(targetModel, {
|
|
1125
|
+
provider: provider ?? providerCfg?.provider,
|
|
1126
|
+
providerPricing: providerCfg?.pricing,
|
|
1127
|
+
customPricing: this.config.customPricing,
|
|
1128
|
+
adapter: this.cachedPricingAdapter
|
|
1129
|
+
});
|
|
1130
|
+
if (!price) {
|
|
1131
|
+
throw new ConfigurationError(
|
|
1132
|
+
`[AvantGate Configuration Error] 'maxCostUSD' was set to $${this.config.maxCostUSD}, but no pricing was configured for model '${targetModel}'. Please define pricing in ProviderConfig, customPricing, or via PricingAdapter.`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
evaluatePessimisticSolvency = (estimatedPromptCost, parentRunId) => {
|
|
1137
|
+
if (!parentRunId || this.config.maxCostUSD === void 0) return;
|
|
1138
|
+
let hasUnconfirmed = false;
|
|
1139
|
+
let settledSpend = 0;
|
|
1140
|
+
for (const r of this.reservations.values()) {
|
|
1141
|
+
if (r.parentRunId !== parentRunId) continue;
|
|
1142
|
+
if (r.status === "UNCONFIRMED_TIMEOUT") hasUnconfirmed = true;
|
|
1143
|
+
if (r.status === "SETTLED") {
|
|
1144
|
+
settledSpend += r.settledCostUsd ?? r.estimatedCostUsd;
|
|
905
1145
|
}
|
|
906
1146
|
}
|
|
1147
|
+
if (!hasUnconfirmed) return;
|
|
1148
|
+
const totalWorstCaseCost = settledSpend + 2 * estimatedPromptCost;
|
|
1149
|
+
if (totalWorstCaseCost > this.config.maxCostUSD) {
|
|
1150
|
+
throw new BudgetExceededError(
|
|
1151
|
+
`[AvantGate FinOps Guard] Retry rejected: worst-case cumulative spend ($${totalWorstCaseCost.toFixed(4)}) exceeds max budget ($${this.config.maxCostUSD.toFixed(4)}).`
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
createBudgetReservation = (estimatedCostUsd, parentRunId) => {
|
|
1156
|
+
const reservationId = `res_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
1157
|
+
const reservation = {
|
|
1158
|
+
id: reservationId,
|
|
1159
|
+
parentRunId,
|
|
1160
|
+
estimatedCostUsd,
|
|
1161
|
+
status: "RESERVED",
|
|
1162
|
+
createdAt: Date.now(),
|
|
1163
|
+
updatedAt: Date.now()
|
|
1164
|
+
};
|
|
1165
|
+
if (this.reservations.size >= 1e4) {
|
|
1166
|
+
const oldestKey = this.reservations.keys().next().value;
|
|
1167
|
+
if (oldestKey) this.reservations.delete(oldestKey);
|
|
1168
|
+
}
|
|
1169
|
+
this.reservations.set(reservationId, reservation);
|
|
1170
|
+
return reservation;
|
|
1171
|
+
};
|
|
1172
|
+
checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider, parentRunId) => {
|
|
1173
|
+
this.validateTokenBudget(estimatedPromptTokens);
|
|
1174
|
+
if (this.config.maxCostUSD === void 0) {
|
|
1175
|
+
return void 0;
|
|
1176
|
+
}
|
|
1177
|
+
await this.validatePricingConfigured(targetModel, provider);
|
|
1178
|
+
const estimatedPromptCost = this.calculateCost(targetModel, estimatedPromptTokens, 0, 0, provider);
|
|
1179
|
+
this.evaluatePessimisticSolvency(estimatedPromptCost, parentRunId);
|
|
1180
|
+
if (estimatedPromptCost > this.config.maxCostUSD) {
|
|
1181
|
+
throw new BudgetExceededError(
|
|
1182
|
+
`[AvantGate Budget Guard] Pre-flight cost budget exceeded: estimated prompt cost ($${estimatedPromptCost.toFixed(6)}) exceeds maxCostUSD ($${this.config.maxCostUSD}).`
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
return this.createBudgetReservation(estimatedPromptCost, parentRunId);
|
|
907
1186
|
};
|
|
908
1187
|
checkPostExecutionBudget = (tokensTotal, costUSD) => {
|
|
909
1188
|
if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
|
|
@@ -937,11 +1216,7 @@ var AvantGateControlLayer = class {
|
|
|
937
1216
|
};
|
|
938
1217
|
};
|
|
939
1218
|
getProviderChain = () => {
|
|
940
|
-
return
|
|
941
|
-
this.config.primary,
|
|
942
|
-
this.config.fallback,
|
|
943
|
-
this.config.emergencyFallback
|
|
944
|
-
].filter((provider) => Boolean(provider?.client));
|
|
1219
|
+
return this.cachedProviderChain;
|
|
945
1220
|
};
|
|
946
1221
|
executeSimulation = (query, systemPrompt, modelOverride) => {
|
|
947
1222
|
const targetModel = modelOverride ?? this.config.primary.model;
|
|
@@ -1105,32 +1380,69 @@ var AvantGateControlLayer = class {
|
|
|
1105
1380
|
runAfterHooks = async (beforeState, output) => {
|
|
1106
1381
|
const tokens = { prompt: output.usage.promptTokens, completion: output.usage.completionTokens, total: output.usage.totalTokens };
|
|
1107
1382
|
const costUSD = this.calculateCost(output.modelUsed, output.usage.promptTokens, output.usage.completionTokens);
|
|
1108
|
-
return executeAfterResponseHooks(
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1383
|
+
return executeAfterResponseHooks(
|
|
1384
|
+
this.config.middlewares,
|
|
1385
|
+
{
|
|
1386
|
+
messages: beforeState.messages,
|
|
1387
|
+
responseText: output.responseText,
|
|
1388
|
+
modelUsed: output.modelUsed,
|
|
1389
|
+
tokens,
|
|
1390
|
+
costUSD,
|
|
1391
|
+
attempts: output.attempts,
|
|
1392
|
+
failoverOccurred: output.failoverOccurred,
|
|
1393
|
+
metadata: beforeState.metadata
|
|
1394
|
+
},
|
|
1395
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
1396
|
+
);
|
|
1397
|
+
};
|
|
1398
|
+
verifyPromptBudget = async (messages, model, parentRunId) => {
|
|
1399
|
+
const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
1400
|
+
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
1401
|
+
return this.checkPreflightBudget(estimatedPromptTokens, model, void 0, parentRunId);
|
|
1118
1402
|
};
|
|
1119
1403
|
execute = async (options) => {
|
|
1120
1404
|
const inputMessages = this.prepareInputMessages(options);
|
|
1121
|
-
const beforeState = await executeBeforeRequestHooks(
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1405
|
+
const beforeState = await executeBeforeRequestHooks(
|
|
1406
|
+
this.config.middlewares,
|
|
1407
|
+
{
|
|
1408
|
+
messages: inputMessages,
|
|
1409
|
+
model: this.config.primary.model,
|
|
1410
|
+
temperature: options.temperature,
|
|
1411
|
+
metadata: options.metadata
|
|
1412
|
+
},
|
|
1413
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
1414
|
+
);
|
|
1127
1415
|
if (beforeState.shortCircuit) {
|
|
1128
1416
|
return this.handleShortCircuit(beforeState.shortCircuit, this.config.primary.model);
|
|
1129
1417
|
}
|
|
1130
|
-
|
|
1131
|
-
const
|
|
1418
|
+
beforeState.messages = this.reScanMessagesAfterHooks(inputMessages, beforeState.messages);
|
|
1419
|
+
const parentRunId = beforeState.metadata?.parentRunId ?? options.metadata?.parentRunId;
|
|
1420
|
+
const reservation = await this.verifyPromptBudget(beforeState.messages, this.config.primary.model, parentRunId);
|
|
1421
|
+
let output;
|
|
1422
|
+
try {
|
|
1423
|
+
output = await this.dispatchPromptExecution(beforeState.messages, options.temperature, options.providerOverride);
|
|
1424
|
+
} catch (err) {
|
|
1425
|
+
if (reservation) {
|
|
1426
|
+
const errorMsg = String(err);
|
|
1427
|
+
const isClientError = errorMsg.includes("HTTP 400") || errorMsg.includes("HTTP 401") || errorMsg.includes("HTTP 403") || errorMsg.includes("HTTP 422");
|
|
1428
|
+
if (isClientError) {
|
|
1429
|
+
reservation.status = "RELEASED";
|
|
1430
|
+
reservation.updatedAt = Date.now();
|
|
1431
|
+
} else {
|
|
1432
|
+
reservation.status = "UNCONFIRMED_TIMEOUT";
|
|
1433
|
+
reservation.updatedAt = Date.now();
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
throw err;
|
|
1437
|
+
}
|
|
1132
1438
|
const afterState = await this.runAfterHooks(beforeState, output);
|
|
1133
|
-
|
|
1439
|
+
const finalResult = await this.finalizeExecutionResult(output, afterState.responseText);
|
|
1440
|
+
if (reservation) {
|
|
1441
|
+
reservation.status = "SETTLED";
|
|
1442
|
+
reservation.settledCostUsd = finalResult.costUSD;
|
|
1443
|
+
reservation.updatedAt = Date.now();
|
|
1444
|
+
}
|
|
1445
|
+
return finalResult;
|
|
1134
1446
|
};
|
|
1135
1447
|
executePrompt = this.execute;
|
|
1136
1448
|
executeStructured = async (options) => {
|
|
@@ -1164,11 +1476,6 @@ var AvantGateControlLayer = class {
|
|
|
1164
1476
|
return msg;
|
|
1165
1477
|
});
|
|
1166
1478
|
};
|
|
1167
|
-
verifyPromptBudget = async (messages, model) => {
|
|
1168
|
-
const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
1169
|
-
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
1170
|
-
await this.checkPreflightBudget(estimatedPromptTokens, model);
|
|
1171
|
-
};
|
|
1172
1479
|
dispatchOverrideAttempt = async (override, messages, model, temperature) => {
|
|
1173
1480
|
const res = await override.complete({ model, messages, temperature: temperature ?? 0.1 });
|
|
1174
1481
|
const usage = this.resolveTokens(res.usage, JSON.stringify(messages), res.text);
|
|
@@ -1291,15 +1598,19 @@ var AvantGateControlLayer = class {
|
|
|
1291
1598
|
runStructuredAfterHooks = async (messages, output, attempt) => {
|
|
1292
1599
|
const tokens = { prompt: output.promptTokens, completion: output.completionTokens, total: output.promptTokens + output.completionTokens };
|
|
1293
1600
|
const costUSD = this.calculateCost(output.modelUsed, output.promptTokens, output.completionTokens);
|
|
1294
|
-
return executeAfterResponseHooks(
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1601
|
+
return executeAfterResponseHooks(
|
|
1602
|
+
this.config.middlewares,
|
|
1603
|
+
{
|
|
1604
|
+
messages,
|
|
1605
|
+
responseText: output.responseText,
|
|
1606
|
+
modelUsed: output.modelUsed,
|
|
1607
|
+
tokens,
|
|
1608
|
+
costUSD,
|
|
1609
|
+
attempts: attempt,
|
|
1610
|
+
failoverOccurred: output.failoverOccurred
|
|
1611
|
+
},
|
|
1612
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
1613
|
+
);
|
|
1303
1614
|
};
|
|
1304
1615
|
executeStructuredLoop = async (options, messages, targetModel, maxRetries) => {
|
|
1305
1616
|
let lastError;
|
|
@@ -1333,16 +1644,42 @@ var AvantGateControlLayer = class {
|
|
|
1333
1644
|
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
1334
1645
|
const targetModel = options.model ?? this.config.primary.model;
|
|
1335
1646
|
const processedMessages = this.sanitizeIncomingMessages(options.messages);
|
|
1336
|
-
const beforeState = await executeBeforeRequestHooks(
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1647
|
+
const beforeState = await executeBeforeRequestHooks(
|
|
1648
|
+
this.config.middlewares,
|
|
1649
|
+
{
|
|
1650
|
+
messages: processedMessages,
|
|
1651
|
+
model: targetModel,
|
|
1652
|
+
temperature: options.temperature
|
|
1653
|
+
},
|
|
1654
|
+
this.config.middlewareTimeoutMs ?? 5e3
|
|
1655
|
+
);
|
|
1341
1656
|
if (beforeState.shortCircuit) {
|
|
1342
1657
|
return this.handleStructuredShortCircuit(beforeState.shortCircuit, options, targetModel);
|
|
1343
1658
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1659
|
+
beforeState.messages = this.reScanMessagesAfterHooks(processedMessages, beforeState.messages);
|
|
1660
|
+
const reservation = await this.verifyPromptBudget(beforeState.messages, targetModel);
|
|
1661
|
+
try {
|
|
1662
|
+
const result = await this.executeStructuredLoop(options, beforeState.messages, targetModel, maxRetries);
|
|
1663
|
+
if (reservation) {
|
|
1664
|
+
reservation.status = "SETTLED";
|
|
1665
|
+
reservation.settledCostUsd = result.costUSD;
|
|
1666
|
+
reservation.updatedAt = Date.now();
|
|
1667
|
+
}
|
|
1668
|
+
return result;
|
|
1669
|
+
} catch (err) {
|
|
1670
|
+
if (reservation) {
|
|
1671
|
+
const errorMsg = String(err);
|
|
1672
|
+
const isClientError = errorMsg.includes("HTTP 400") || errorMsg.includes("HTTP 401") || errorMsg.includes("HTTP 403") || errorMsg.includes("HTTP 422");
|
|
1673
|
+
if (isClientError) {
|
|
1674
|
+
reservation.status = "RELEASED";
|
|
1675
|
+
reservation.updatedAt = Date.now();
|
|
1676
|
+
} else {
|
|
1677
|
+
reservation.status = "UNCONFIRMED_TIMEOUT";
|
|
1678
|
+
reservation.updatedAt = Date.now();
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
throw err;
|
|
1682
|
+
}
|
|
1346
1683
|
};
|
|
1347
1684
|
};
|
|
1348
1685
|
var createLLMControlLayer = (config) => {
|
|
@@ -1431,7 +1768,7 @@ var PromptTemplate = class {
|
|
|
1431
1768
|
};
|
|
1432
1769
|
|
|
1433
1770
|
// src/prompts/prompt-builder.ts
|
|
1434
|
-
import { z } from "zod";
|
|
1771
|
+
import { z as z2 } from "zod";
|
|
1435
1772
|
var PromptBuilder = class {
|
|
1436
1773
|
personaSlot = "";
|
|
1437
1774
|
rulesSlot = [];
|
|
@@ -1487,7 +1824,7 @@ var PromptBuilder = class {
|
|
|
1487
1824
|
}
|
|
1488
1825
|
schemaContract(schema, options) {
|
|
1489
1826
|
let shapeDescription = "JSON Object";
|
|
1490
|
-
if (schema instanceof
|
|
1827
|
+
if (schema instanceof z2.ZodObject) {
|
|
1491
1828
|
shapeDescription = JSON.stringify(Object.keys(schema.shape));
|
|
1492
1829
|
}
|
|
1493
1830
|
this.schemaContractText = [
|
|
@@ -1678,6 +2015,8 @@ var createLaunchSafeConfig = (options) => {
|
|
|
1678
2015
|
return {
|
|
1679
2016
|
maxTokenBudget: 8192,
|
|
1680
2017
|
maxCostUSD: 0.5,
|
|
2018
|
+
middlewareTimeoutMs: 5e3,
|
|
2019
|
+
reScanIngressAfterHooks: true,
|
|
1681
2020
|
...options,
|
|
1682
2021
|
retryOptions: { ...defaultRetries, ...options.retryOptions },
|
|
1683
2022
|
security: { ...defaultSecurity, ...options.security }
|
|
@@ -1699,6 +2038,8 @@ var createEnterpriseStrictConfig = (options) => {
|
|
|
1699
2038
|
return {
|
|
1700
2039
|
maxTokenBudget: 4096,
|
|
1701
2040
|
maxCostUSD: 0.15,
|
|
2041
|
+
middlewareTimeoutMs: 3e3,
|
|
2042
|
+
reScanIngressAfterHooks: true,
|
|
1702
2043
|
...options,
|
|
1703
2044
|
retryOptions: { ...defaultRetries, ...options.retryOptions },
|
|
1704
2045
|
security: { ...defaultSecurity, ...options.security }
|
|
@@ -1734,14 +2075,22 @@ export {
|
|
|
1734
2075
|
SecretLeakBlockedError as AvantGateSecretLeakBlockedError,
|
|
1735
2076
|
BudgetExceededError,
|
|
1736
2077
|
CachedPricingAdapter,
|
|
2078
|
+
ChatMessageSchema,
|
|
2079
|
+
ChatMessagesArraySchema,
|
|
2080
|
+
ChatRoleSchema,
|
|
1737
2081
|
ConfigurationError,
|
|
1738
2082
|
DEFAULT_MODEL_PRICES,
|
|
1739
2083
|
HttpProviderClient,
|
|
2084
|
+
InvalidMessageSchemaError,
|
|
2085
|
+
MiddlewareTimeoutError,
|
|
1740
2086
|
PRESETS,
|
|
1741
2087
|
PricingRegistry,
|
|
1742
2088
|
PromptBuilder,
|
|
2089
|
+
PromptInjectionError,
|
|
1743
2090
|
PromptRegistry,
|
|
1744
2091
|
PromptTemplate,
|
|
2092
|
+
ProviderIdleTimeoutError,
|
|
2093
|
+
ProviderTTFTTimeoutError,
|
|
1745
2094
|
SEED_MODEL_PRICES,
|
|
1746
2095
|
SecretLeakBlockedError,
|
|
1747
2096
|
SqlitePricingAdapter,
|
|
@@ -1759,6 +2108,7 @@ export {
|
|
|
1759
2108
|
createHttpProviderClient,
|
|
1760
2109
|
createLLMControlLayer,
|
|
1761
2110
|
createLaunchSafeConfig,
|
|
2111
|
+
deepFreeze,
|
|
1762
2112
|
defineWorkflow,
|
|
1763
2113
|
executeAfterResponseHooks,
|
|
1764
2114
|
executeBeforeRequestHooks,
|