@anuma/sdk 6.24.1 → 6.25.1
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/expo/{chunk-XDBX4FZY.mjs → chunk-EDABJLYN.mjs} +441 -2
- package/dist/{react/consolidate-XKS6IZFC.mjs → expo/consolidate-5WM3CFKK.mjs} +2 -4
- package/dist/expo/index.cjs +16259 -16469
- package/dist/expo/index.d.mts +106 -18
- package/dist/expo/index.d.ts +106 -18
- package/dist/expo/index.mjs +14022 -5927
- package/dist/index.cjs +88 -46
- package/dist/index.mjs +88 -46
- package/dist/memory/context.d.mts +20 -1
- package/dist/memory/context.d.ts +20 -1
- package/dist/{server/chunk-RKQ4MRS2.mjs → react/chunk-BPMINZPO.mjs} +443 -2
- package/dist/{expo/consolidate-XBYKAFKT.mjs → react/consolidate-A2X3M47N.mjs} +2 -4
- package/dist/react/index.cjs +17892 -18131
- package/dist/react/index.d.mts +113 -18
- package/dist/react/index.d.ts +113 -18
- package/dist/react/index.mjs +11492 -3153
- package/dist/{react/chunk-GPLMMMZN.mjs → server/chunk-DWNEDXA5.mjs} +425 -4
- package/dist/server/{consolidate-54P6QZKU.mjs → consolidate-53I4CTD7.mjs} +2 -4
- package/dist/server/index.cjs +11333 -11520
- package/dist/server/index.d.mts +113 -18
- package/dist/server/index.d.ts +113 -18
- package/dist/server/index.mjs +26288 -4192
- package/dist/telemetry/index.d.mts +4 -0
- package/dist/telemetry/index.d.ts +4 -0
- package/dist/tools/index.cjs +112 -2
- package/dist/tools/index.d.mts +106 -1
- package/dist/tools/index.d.ts +106 -1
- package/dist/tools/index.mjs +110 -2
- package/dist/tools/selection/index.cjs +88 -46
- package/dist/tools/selection/index.mjs +88 -46
- package/package.json +1 -1
- package/dist/expo/chunk-5LKHK5LN.mjs +0 -8142
- package/dist/expo/chunk-WUG6GTHO.mjs +0 -445
- package/dist/expo/recall-4FSY2MC4.mjs +0 -8
- package/dist/react/chunk-DOYXFYB3.mjs +0 -8472
- package/dist/react/chunk-MHZB6KIX.mjs +0 -445
- package/dist/react/recall-6FVSDGWU.mjs +0 -8
- package/dist/server/chunk-FEDRFVHD.mjs +0 -22161
- package/dist/server/chunk-YXL5VKCO.mjs +0 -429
- package/dist/server/recall-Z2WJGXOL.mjs +0 -8
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
BASE_URL
|
|
3
|
+
} from "./chunk-GYKJOWEO.mjs";
|
|
4
|
+
|
|
5
|
+
// src/lib/logger.ts
|
|
6
|
+
var consoleLogger = {
|
|
7
|
+
debug: console.log.bind(console),
|
|
8
|
+
info: console.info.bind(console),
|
|
9
|
+
warn: console.warn.bind(console),
|
|
10
|
+
error: console.error.bind(console)
|
|
11
|
+
};
|
|
12
|
+
var noopLogger = {
|
|
13
|
+
debug: () => {
|
|
14
|
+
},
|
|
15
|
+
info: () => {
|
|
16
|
+
},
|
|
17
|
+
warn: () => {
|
|
18
|
+
},
|
|
19
|
+
error: () => {
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var currentLogger = consoleLogger;
|
|
23
|
+
function setLogger(logger) {
|
|
24
|
+
currentLogger = logger;
|
|
25
|
+
}
|
|
26
|
+
function getLogger() {
|
|
27
|
+
return currentLogger;
|
|
28
|
+
}
|
|
4
29
|
|
|
5
30
|
// src/lib/pii/patterns.ts
|
|
6
31
|
function luhnCheck(digits) {
|
|
@@ -641,6 +666,406 @@ function createStreamingDeAnonymizer(redactor, emit) {
|
|
|
641
666
|
};
|
|
642
667
|
}
|
|
643
668
|
|
|
669
|
+
// src/lib/internalFlowMarker.ts
|
|
670
|
+
var INTERNAL_FLOW_MARKER = "Anuma internal first-party flow (not user chat).";
|
|
671
|
+
function withInternalFlowMarker(systemPrompt) {
|
|
672
|
+
if (systemPrompt.includes(INTERNAL_FLOW_MARKER)) return systemPrompt;
|
|
673
|
+
return `${INTERNAL_FLOW_MARKER}
|
|
674
|
+
${systemPrompt}`;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/lib/chat/endpointOverride.ts
|
|
678
|
+
function validateEndpointOverride(override) {
|
|
679
|
+
const trimmed = override.trim();
|
|
680
|
+
if (trimmed === "") {
|
|
681
|
+
return {
|
|
682
|
+
valid: false,
|
|
683
|
+
message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (trimmed.startsWith("//") || trimmed.includes("://")) {
|
|
687
|
+
return {
|
|
688
|
+
valid: false,
|
|
689
|
+
message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/lib/taskType.ts
|
|
696
|
+
var TASK_TYPE_HEADER = "X-Anuma-Task-Type";
|
|
697
|
+
function taskTypeHeader(taskType) {
|
|
698
|
+
return taskType ? { [TASK_TYPE_HEADER]: taskType } : {};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/lib/memory/portalLlm.ts
|
|
702
|
+
function defaultBaseUrl() {
|
|
703
|
+
return typeof process !== "undefined" && process.env?.ANUMA_PORTAL_BASE_URL || BASE_URL;
|
|
704
|
+
}
|
|
705
|
+
var RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
706
|
+
var RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
707
|
+
var JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
708
|
+
function supportsResponseFormat(model, variant = "json_object") {
|
|
709
|
+
const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
|
|
710
|
+
return model.split("/").some((seg) => allow.has(seg));
|
|
711
|
+
}
|
|
712
|
+
var RESPONSES_ONLY_MODEL_PREFIXES = ["gpt-5.6"];
|
|
713
|
+
function requiresResponsesTransport(model) {
|
|
714
|
+
return model.split("/").some((seg) => RESPONSES_ONLY_MODEL_PREFIXES.some((prefix) => seg.startsWith(prefix)));
|
|
715
|
+
}
|
|
716
|
+
var CHAT_TO_RESPONSES_FIELDS = {
|
|
717
|
+
max_completion_tokens: "max_output_tokens"
|
|
718
|
+
};
|
|
719
|
+
function responsesExtra(extra) {
|
|
720
|
+
if (!extra) return {};
|
|
721
|
+
const out = {};
|
|
722
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
723
|
+
const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
|
|
724
|
+
if (target !== key && target in extra) continue;
|
|
725
|
+
out[target] = value;
|
|
726
|
+
}
|
|
727
|
+
return out;
|
|
728
|
+
}
|
|
729
|
+
var FOREIGN_ENDPOINT_SUFFIX = {
|
|
730
|
+
chat: "/responses",
|
|
731
|
+
responses: "/chat/completions"
|
|
732
|
+
};
|
|
733
|
+
function assertTransportMatchesEndpoint(transport, endpoint) {
|
|
734
|
+
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
735
|
+
if (!endpoint.endsWith(foreign)) return;
|
|
736
|
+
throw new Error(
|
|
737
|
+
`endpointOverride "${endpoint}" is the other transport's endpoint, but transport is "${transport}". The request body is built for the transport, so this sends the wrong shape and 400s without retry.`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
var RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
|
|
741
|
+
function isRetryableStatus(status) {
|
|
742
|
+
return status >= 500 || RETRYABLE_HTTP.has(status);
|
|
743
|
+
}
|
|
744
|
+
function parseRetryAfterMs(header) {
|
|
745
|
+
if (!header) return null;
|
|
746
|
+
const secs = Number(header);
|
|
747
|
+
if (Number.isFinite(secs)) return Math.max(0, secs * 1e3);
|
|
748
|
+
const dateMs = Date.parse(header);
|
|
749
|
+
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
function defaultBackoffMs(attempt) {
|
|
753
|
+
const base = Math.min(2e3, 250 * 2 ** (attempt - 1));
|
|
754
|
+
return base + Math.floor(Math.random() * 100);
|
|
755
|
+
}
|
|
756
|
+
function sleep(ms) {
|
|
757
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
758
|
+
}
|
|
759
|
+
async function resolvePortalAuthHeaders(auth, tag) {
|
|
760
|
+
if (auth.apiKey) {
|
|
761
|
+
return { "x-api-key": auth.apiKey };
|
|
762
|
+
}
|
|
763
|
+
if (auth.getToken) {
|
|
764
|
+
let token;
|
|
765
|
+
try {
|
|
766
|
+
token = await auth.getToken();
|
|
767
|
+
} catch (err) {
|
|
768
|
+
getLogger().warn(`[${tag}] getToken threw`, err);
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
if (!token) {
|
|
772
|
+
getLogger().warn(`[${tag}] getToken returned no token`);
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
return { Authorization: `Bearer ${token}` };
|
|
776
|
+
}
|
|
777
|
+
throw new Error(`[${tag}] Either apiKey or getToken must be provided`);
|
|
778
|
+
}
|
|
779
|
+
async function callPortalJsonCompletion(req) {
|
|
780
|
+
const log = getLogger();
|
|
781
|
+
let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
|
|
782
|
+
if (req.endpointOverride !== void 0) {
|
|
783
|
+
const overrideValidation = validateEndpointOverride(req.endpointOverride);
|
|
784
|
+
if (!overrideValidation.valid) {
|
|
785
|
+
throw new Error(overrideValidation.message);
|
|
786
|
+
}
|
|
787
|
+
endpoint = overrideValidation.endpoint;
|
|
788
|
+
assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
|
|
789
|
+
}
|
|
790
|
+
const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
|
|
791
|
+
const startedAt = Date.now();
|
|
792
|
+
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
793
|
+
let lastFailure;
|
|
794
|
+
let reinforce = false;
|
|
795
|
+
const giveUp = () => {
|
|
796
|
+
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
797
|
+
return null;
|
|
798
|
+
};
|
|
799
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
800
|
+
if (attempt > 1 && overBudget()) {
|
|
801
|
+
log.warn(`[${req.tag}] over time budget before attempt ${attempt}, giving up`);
|
|
802
|
+
lastFailure = { reason: "time-budget-exhausted", attempts: attempt - 1 };
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
const attemptReq = req.totalTimeoutMs !== void 0 ? {
|
|
806
|
+
...req,
|
|
807
|
+
timeoutMs: Math.max(
|
|
808
|
+
1,
|
|
809
|
+
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
810
|
+
),
|
|
811
|
+
...reinforce && { reinforceJsonContract: true }
|
|
812
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
813
|
+
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
814
|
+
if (outcome.kind === "ok") {
|
|
815
|
+
req.onAttempt?.({ attempt, ok: true });
|
|
816
|
+
return outcome.value;
|
|
817
|
+
}
|
|
818
|
+
req.onAttempt?.({
|
|
819
|
+
attempt,
|
|
820
|
+
ok: false,
|
|
821
|
+
reason: outcome.code,
|
|
822
|
+
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus }
|
|
823
|
+
});
|
|
824
|
+
lastFailure = {
|
|
825
|
+
reason: outcome.code,
|
|
826
|
+
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
827
|
+
attempts: attempt
|
|
828
|
+
};
|
|
829
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
830
|
+
if (outcome.kind === "terminal") {
|
|
831
|
+
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
832
|
+
return giveUp();
|
|
833
|
+
}
|
|
834
|
+
if (attempt >= maxAttempts) {
|
|
835
|
+
log.warn(`[${req.tag}] ${outcome.reason} \u2014 attempt ${attempt}/${maxAttempts}, giving up`);
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
const backoff = (req.backoffMs ?? defaultBackoffMs)(attempt);
|
|
839
|
+
const delay = Math.max(backoff, outcome.retryAfterMs ?? 0);
|
|
840
|
+
if (overBudget() || req.totalTimeoutMs !== void 0 && Date.now() - startedAt + delay >= req.totalTimeoutMs) {
|
|
841
|
+
log.warn(
|
|
842
|
+
`[${req.tag}] ${outcome.reason} \u2014 attempt ${attempt}/${maxAttempts}, over time budget, giving up`
|
|
843
|
+
);
|
|
844
|
+
lastFailure = { reason: "time-budget-exhausted", attempts: attempt };
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
log.warn(`[${req.tag}] ${outcome.reason} \u2014 attempt ${attempt}/${maxAttempts}, retrying`);
|
|
848
|
+
if (delay > 0) await sleep(delay);
|
|
849
|
+
}
|
|
850
|
+
return giveUp();
|
|
851
|
+
}
|
|
852
|
+
async function attemptPortalJson(req, endpoint) {
|
|
853
|
+
const baseUrl = req.baseUrl ?? defaultBaseUrl();
|
|
854
|
+
const fetchImpl = req.fetchFn ?? fetch;
|
|
855
|
+
const timeoutMs = req.timeoutMs ?? 6e4;
|
|
856
|
+
const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
|
|
857
|
+
if (authHeaders === null)
|
|
858
|
+
return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
|
|
859
|
+
const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
|
|
860
|
+
const prefill = usesPrefill ? "{" : "";
|
|
861
|
+
const messages = [
|
|
862
|
+
// Every caller of this helper is a first-party BACKGROUND op (fact extraction,
|
|
863
|
+
// topic sweep, consolidation, classifiers, graph traversal, query
|
|
864
|
+
// decomposition). Marking here — the one place they all funnel through — is what
|
|
865
|
+
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
866
|
+
// See ../internalFlowMarker.
|
|
867
|
+
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
868
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
869
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
870
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
871
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
872
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
873
|
+
{ role: "user", content: req.userMessage }
|
|
874
|
+
];
|
|
875
|
+
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
876
|
+
const supportsJsonObjectFormat = supportsResponseFormat(req.model);
|
|
877
|
+
const controller = new AbortController();
|
|
878
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
879
|
+
const requestBody = req.transport === "responses" ? {
|
|
880
|
+
model: req.model,
|
|
881
|
+
// Same system+user pair under the Responses-API field name. No prefill
|
|
882
|
+
// entry to strip: `usesPrefill` is false on this transport, so it was
|
|
883
|
+
// never pushed and the response-side restore is disarmed with it. An
|
|
884
|
+
// earlier version pushed it and sliced it back off here, which left
|
|
885
|
+
// `prefill` truthy and the restore still armed — the fix belonged at
|
|
886
|
+
// the decision, not at the two symptoms.
|
|
887
|
+
input: messages,
|
|
888
|
+
...req.reasoning && { reasoning: req.reasoning },
|
|
889
|
+
// NO response_format. The Responses API spells structured output
|
|
890
|
+
// differently (`text.format`), and it is unverified against this
|
|
891
|
+
// portal — so this transport relies on the same strict-JSON system
|
|
892
|
+
// prompt plus `extractJsonCandidate` that every response_format-
|
|
893
|
+
// rejecting model already relies on. Verified end to end on dev:
|
|
894
|
+
// luna returns clean parseable JSON here without it. Add the field
|
|
895
|
+
// only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
|
|
896
|
+
// earned.
|
|
897
|
+
...responsesExtra(req.extra)
|
|
898
|
+
} : {
|
|
899
|
+
model: req.model,
|
|
900
|
+
messages,
|
|
901
|
+
...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
|
|
902
|
+
...req.extra
|
|
903
|
+
};
|
|
904
|
+
if (req.transport === "responses" || !supportsJsonObjectFormat) {
|
|
905
|
+
delete requestBody.response_format;
|
|
906
|
+
}
|
|
907
|
+
let response;
|
|
908
|
+
try {
|
|
909
|
+
response = await fetchImpl(`${baseUrl}${endpoint}`, {
|
|
910
|
+
method: "POST",
|
|
911
|
+
// Attached HERE, at the one transport every background memory op shares, so
|
|
912
|
+
// a new flow gets the provenance by construction rather than by remembering.
|
|
913
|
+
headers: {
|
|
914
|
+
...authHeaders,
|
|
915
|
+
...taskTypeHeader(req.taskType),
|
|
916
|
+
"Content-Type": "application/json"
|
|
917
|
+
},
|
|
918
|
+
body: JSON.stringify(requestBody),
|
|
919
|
+
signal: controller.signal
|
|
920
|
+
});
|
|
921
|
+
} catch (err) {
|
|
922
|
+
clearTimeout(timer);
|
|
923
|
+
return {
|
|
924
|
+
kind: "retryable",
|
|
925
|
+
code: "network",
|
|
926
|
+
reason: `fetch failed: ${err.message}`
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
if (!response.ok) {
|
|
930
|
+
clearTimeout(timer);
|
|
931
|
+
const reason = `portal returned ${response.status}`;
|
|
932
|
+
if (response.status === 401) {
|
|
933
|
+
const tokenAuth = !req.apiKey && !!req.getToken;
|
|
934
|
+
return tokenAuth ? {
|
|
935
|
+
kind: "retryable",
|
|
936
|
+
code: "http-retryable",
|
|
937
|
+
reason: `${reason} (token may be expired; refreshing)`,
|
|
938
|
+
httpStatus: response.status
|
|
939
|
+
} : { kind: "terminal", code: "http-terminal", reason, httpStatus: response.status };
|
|
940
|
+
}
|
|
941
|
+
if (!isRetryableStatus(response.status))
|
|
942
|
+
return { kind: "terminal", code: "http-terminal", reason, httpStatus: response.status };
|
|
943
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
944
|
+
return {
|
|
945
|
+
kind: "retryable",
|
|
946
|
+
code: "http-retryable",
|
|
947
|
+
reason,
|
|
948
|
+
httpStatus: response.status,
|
|
949
|
+
...retryAfterMs !== null && { retryAfterMs }
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
let body;
|
|
953
|
+
try {
|
|
954
|
+
body = await response.json();
|
|
955
|
+
} catch (err) {
|
|
956
|
+
clearTimeout(timer);
|
|
957
|
+
return {
|
|
958
|
+
kind: "retryable",
|
|
959
|
+
code: "body-parse-failed",
|
|
960
|
+
reason: `response body parse failed: ${err.message}`
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
clearTimeout(timer);
|
|
964
|
+
logPortalUsage(body, req.tag);
|
|
965
|
+
const rawContent = extractCompletionContent(body);
|
|
966
|
+
if (!rawContent) {
|
|
967
|
+
return {
|
|
968
|
+
kind: "retryable",
|
|
969
|
+
code: "empty-content",
|
|
970
|
+
reason: "portal response had no completion content"
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
const looksLikeContinuation = prefill && /^\s*"/.test(rawContent);
|
|
974
|
+
const content = looksLikeContinuation ? prefill + rawContent : rawContent;
|
|
975
|
+
const candidate = extractJsonCandidate(content);
|
|
976
|
+
let value;
|
|
977
|
+
try {
|
|
978
|
+
value = JSON.parse(candidate);
|
|
979
|
+
} catch (err) {
|
|
980
|
+
return {
|
|
981
|
+
kind: "retryable",
|
|
982
|
+
code: "invalid-json",
|
|
983
|
+
reason: `completion was not valid JSON: ${err.message}`
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (value === null) {
|
|
987
|
+
return { kind: "retryable", code: "null-completion", reason: "completion parsed to null" };
|
|
988
|
+
}
|
|
989
|
+
return { kind: "ok", value };
|
|
990
|
+
}
|
|
991
|
+
function extractJsonCandidate(raw) {
|
|
992
|
+
const trimmed = raw.trim();
|
|
993
|
+
const fence = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
994
|
+
const body = fence ? fence[1].trim() : trimmed;
|
|
995
|
+
const start = body.search(/[{[]/);
|
|
996
|
+
if (start < 0) return body;
|
|
997
|
+
const open = body[start];
|
|
998
|
+
const close = open === "{" ? "}" : "]";
|
|
999
|
+
let depth = 0;
|
|
1000
|
+
let inString = false;
|
|
1001
|
+
let escape = false;
|
|
1002
|
+
for (let i = start; i < body.length; i++) {
|
|
1003
|
+
const ch = body[i];
|
|
1004
|
+
if (escape) {
|
|
1005
|
+
escape = false;
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
if (ch === "\\" && inString) {
|
|
1009
|
+
escape = true;
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (ch === '"') {
|
|
1013
|
+
inString = !inString;
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (inString) continue;
|
|
1017
|
+
if (ch === open) depth++;
|
|
1018
|
+
else if (ch === close) {
|
|
1019
|
+
depth--;
|
|
1020
|
+
if (depth === 0) return body.slice(start, i + 1);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return body;
|
|
1024
|
+
}
|
|
1025
|
+
function asRecord(value) {
|
|
1026
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1027
|
+
}
|
|
1028
|
+
function asCount(value) {
|
|
1029
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1030
|
+
}
|
|
1031
|
+
function logPortalUsage(body, tag) {
|
|
1032
|
+
try {
|
|
1033
|
+
const root = asRecord(body);
|
|
1034
|
+
if (!root) return;
|
|
1035
|
+
const usage = asRecord(root.usage);
|
|
1036
|
+
const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
|
|
1037
|
+
const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
|
|
1038
|
+
if (promptTokens === void 0 && completionTokens === void 0) return;
|
|
1039
|
+
const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? asCount(asRecord(usage?.input_tokens_details)?.cached_tokens) ?? 0;
|
|
1040
|
+
const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
|
|
1041
|
+
const parts = [];
|
|
1042
|
+
if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
|
|
1043
|
+
if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
|
|
1044
|
+
if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
|
|
1045
|
+
parts.push(`cached=${cachedTokens}`);
|
|
1046
|
+
getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
|
|
1047
|
+
} catch {
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function extractCompletionContent(body) {
|
|
1051
|
+
if (typeof body !== "object" || body === null) return null;
|
|
1052
|
+
const responsesText = extractResponsesContent(body);
|
|
1053
|
+
if (responsesText !== null) return responsesText;
|
|
1054
|
+
const choices = body.choices;
|
|
1055
|
+
if (!Array.isArray(choices) || choices.length === 0) return null;
|
|
1056
|
+
const first = choices[0];
|
|
1057
|
+
const content = first?.message?.content;
|
|
1058
|
+
return typeof content === "string" ? content : null;
|
|
1059
|
+
}
|
|
1060
|
+
function extractResponsesContent(body) {
|
|
1061
|
+
const root = asRecord(body);
|
|
1062
|
+
if (!root) return null;
|
|
1063
|
+
if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
|
|
1064
|
+
if (!Array.isArray(root.output)) return null;
|
|
1065
|
+
const text = root.output.filter((item) => asRecord(item)?.type === "message").flatMap((item) => Array.isArray(item.content) ? item.content : []).map((part) => asRecord(part)?.text).filter((t) => typeof t === "string").join("");
|
|
1066
|
+
return text;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
644
1069
|
// src/lib/memory/consolidationFallback.ts
|
|
645
1070
|
function notifyConsolidationFallback(reason, onFallback, detail) {
|
|
646
1071
|
const refused = reason === "subject_mismatch";
|
|
@@ -661,7 +1086,23 @@ function notifyConsolidationFallback(reason, onFallback, detail) {
|
|
|
661
1086
|
}
|
|
662
1087
|
|
|
663
1088
|
export {
|
|
1089
|
+
consoleLogger,
|
|
1090
|
+
noopLogger,
|
|
1091
|
+
setLogger,
|
|
1092
|
+
getLogger,
|
|
1093
|
+
PiiRedactor,
|
|
1094
|
+
isPiiRedactor,
|
|
664
1095
|
resolvePiiRedactor,
|
|
665
1096
|
createStreamingDeAnonymizer,
|
|
1097
|
+
validateEndpointOverride,
|
|
1098
|
+
INTERNAL_FLOW_MARKER,
|
|
1099
|
+
withInternalFlowMarker,
|
|
1100
|
+
taskTypeHeader,
|
|
1101
|
+
supportsResponseFormat,
|
|
1102
|
+
requiresResponsesTransport,
|
|
1103
|
+
resolvePortalAuthHeaders,
|
|
1104
|
+
callPortalJsonCompletion,
|
|
1105
|
+
extractJsonCandidate,
|
|
1106
|
+
extractCompletionContent,
|
|
666
1107
|
notifyConsolidationFallback
|
|
667
1108
|
};
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
callPortalJsonCompletion,
|
|
2
3
|
notifyConsolidationFallback,
|
|
3
4
|
resolvePiiRedactor
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
callPortalJsonCompletion
|
|
7
|
-
} from "./chunk-WUG6GTHO.mjs";
|
|
5
|
+
} from "./chunk-BPMINZPO.mjs";
|
|
8
6
|
import "./chunk-GYKJOWEO.mjs";
|
|
9
7
|
|
|
10
8
|
// src/lib/memory/consolidate.ts
|