@anuma/sdk 6.15.0 → 6.15.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-3V3BBJLH.mjs → chunk-4Z2X4UYQ.mjs} +7 -1
- package/dist/expo/{chunk-DIUQLXEH.mjs → chunk-A2G6TRUK.mjs} +1 -1
- package/dist/expo/{chunk-LUSKQQDD.mjs → chunk-TNMPAE3V.mjs} +2 -2
- package/dist/{react/consolidate-Y2XWEZRQ.mjs → expo/consolidate-ZM5JNULQ.mjs} +2 -2
- package/dist/expo/index.cjs +117 -74
- package/dist/expo/index.mjs +118 -77
- package/dist/expo/{recall-GKYVPD2B.mjs → recall-FJQTIMYT.mjs} +2 -2
- package/dist/react/{chunk-3V3BBJLH.mjs → chunk-4Z2X4UYQ.mjs} +7 -1
- package/dist/react/{chunk-2GNOTRG4.mjs → chunk-7X7BNMGS.mjs} +1 -1
- package/dist/react/{chunk-PDKKPAXP.mjs → chunk-YSL3J4K4.mjs} +2 -2
- package/dist/{expo/consolidate-GTMXZZ4C.mjs → react/consolidate-7NMKF7UC.mjs} +2 -2
- package/dist/react/index.cjs +117 -74
- package/dist/react/index.mjs +118 -77
- package/dist/react/{recall-5HNAFHLL.mjs → recall-UVVZGRVF.mjs} +2 -2
- package/dist/server/{chunk-JPLFOSB2.mjs → chunk-4LPSEROP.mjs} +1 -1
- package/dist/server/{chunk-2QFK4L7Y.mjs → chunk-ARYSRI4Z.mjs} +2 -2
- package/dist/server/{chunk-H4G3DLDJ.mjs → chunk-BSCEZOIW.mjs} +7 -1
- package/dist/server/{consolidate-CMMDTZ37.mjs → consolidate-MSAXASHL.mjs} +2 -2
- package/dist/server/index.cjs +117 -74
- package/dist/server/index.mjs +118 -77
- package/dist/server/{recall-EODNEKIA.mjs → recall-OVSSDRYU.mjs} +2 -2
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -20611,6 +20611,9 @@ function supportsResponseFormat(model, variant = "json_object") {
|
|
|
20611
20611
|
const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
|
|
20612
20612
|
return model.split("/").some((seg) => allow.has(seg));
|
|
20613
20613
|
}
|
|
20614
|
+
function requiresResponsesTransport(model) {
|
|
20615
|
+
return model.split("/").some((seg) => RESPONSES_ONLY_MODEL_PREFIXES.some((prefix) => seg.startsWith(prefix)));
|
|
20616
|
+
}
|
|
20614
20617
|
function responsesExtra(extra) {
|
|
20615
20618
|
if (!extra) return {};
|
|
20616
20619
|
const out = {};
|
|
@@ -20946,7 +20949,7 @@ function extractResponsesContent(body) {
|
|
|
20946
20949
|
const text12 = 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("");
|
|
20947
20950
|
return text12;
|
|
20948
20951
|
}
|
|
20949
|
-
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
|
|
20952
|
+
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RESPONSES_ONLY_MODEL_PREFIXES, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
|
|
20950
20953
|
var init_portalLlm = __esm({
|
|
20951
20954
|
"src/lib/memory/portalLlm.ts"() {
|
|
20952
20955
|
"use strict";
|
|
@@ -20958,6 +20961,7 @@ var init_portalLlm = __esm({
|
|
|
20958
20961
|
RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
20959
20962
|
RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
20960
20963
|
JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
20964
|
+
RESPONSES_ONLY_MODEL_PREFIXES = ["gpt-5.6"];
|
|
20961
20965
|
CHAT_TO_RESPONSES_FIELDS = {
|
|
20962
20966
|
max_completion_tokens: "max_output_tokens"
|
|
20963
20967
|
};
|
|
@@ -30733,6 +30737,9 @@ var DEFAULT_BASE_URL2 = BASE_URL;
|
|
|
30733
30737
|
var DEFAULT_MODEL4 = "anthropic/claude-sonnet-4-6";
|
|
30734
30738
|
var DEFAULT_MAX_TOKENS = 4096;
|
|
30735
30739
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
30740
|
+
var MIN_RETRY_BUDGET_MS = 2e3;
|
|
30741
|
+
var MIN_RESPONSES_OUTPUT_TOKENS = 2048;
|
|
30742
|
+
var SCHEMA_FALLBACK_SKIP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 408, 409, 413, 425, 429]);
|
|
30736
30743
|
var DEFAULT_SYSTEM_PROMPT = `You are a personal assistant with access to the user's memory. Answer the user's question using the supplied memories as evidence.
|
|
30737
30744
|
|
|
30738
30745
|
Rules:
|
|
@@ -30764,14 +30771,9 @@ ${m.content}`).join("\n\n");
|
|
|
30764
30771
|
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL2;
|
|
30765
30772
|
const model = options.llmModel ?? DEFAULT_MODEL4;
|
|
30766
30773
|
const wantsStructured = !!options.responseSchema;
|
|
30767
|
-
const
|
|
30774
|
+
const useResponsesTransport = requiresResponsesTransport(model);
|
|
30775
|
+
const sendResponseFormat = wantsStructured && !useResponsesTransport && supportsResponseFormat(model, "json_schema");
|
|
30768
30776
|
const basePrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
30769
|
-
const systemPrompt = wantsStructured && !sendResponseFormat ? `${basePrompt}
|
|
30770
|
-
|
|
30771
|
-
Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
|
|
30772
|
-
${JSON.stringify(
|
|
30773
|
-
options.responseSchema
|
|
30774
|
-
)}` : basePrompt;
|
|
30775
30777
|
const userMessage = [
|
|
30776
30778
|
`Question:
|
|
30777
30779
|
${trimmed}`,
|
|
@@ -30781,85 +30783,121 @@ ${evidence}`
|
|
|
30781
30783
|
].join("\n\n");
|
|
30782
30784
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
30783
30785
|
const fetchImpl = options.fetchFn ?? fetch;
|
|
30786
|
+
const buildBody = (useResponseFormat) => {
|
|
30787
|
+
const systemPrompt = wantsStructured && !useResponseFormat ? `${basePrompt}
|
|
30788
|
+
|
|
30789
|
+
Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
|
|
30790
|
+
${JSON.stringify(
|
|
30791
|
+
options.responseSchema
|
|
30792
|
+
)}` : basePrompt;
|
|
30793
|
+
const messages = [
|
|
30794
|
+
{ role: "system", content: systemPrompt },
|
|
30795
|
+
{ role: "user", content: userMessage }
|
|
30796
|
+
];
|
|
30797
|
+
if (useResponsesTransport) {
|
|
30798
|
+
return JSON.stringify({
|
|
30799
|
+
model,
|
|
30800
|
+
// Same system+user pair under the Responses-API field names: `input`
|
|
30801
|
+
// for the turns, `max_output_tokens` for the cap. Sending the
|
|
30802
|
+
// chat-completions spelling here is silently ignored, which caps the
|
|
30803
|
+
// answer at the portal default.
|
|
30804
|
+
input: messages,
|
|
30805
|
+
max_output_tokens: Math.max(maxTokens, MIN_RESPONSES_OUTPUT_TOKENS)
|
|
30806
|
+
});
|
|
30807
|
+
}
|
|
30808
|
+
return JSON.stringify({
|
|
30809
|
+
model,
|
|
30810
|
+
// Modern OpenAI field; the portal reads only `max_completion_tokens`
|
|
30811
|
+
// (the deprecated `max_tokens` is silently ignored → falls back to the
|
|
30812
|
+
// portal's default output cap and truncates the answer).
|
|
30813
|
+
max_completion_tokens: maxTokens,
|
|
30814
|
+
messages,
|
|
30815
|
+
...useResponseFormat && {
|
|
30816
|
+
response_format: {
|
|
30817
|
+
type: "json_schema",
|
|
30818
|
+
json_schema: { name: "reflect_output", schema: options.responseSchema }
|
|
30819
|
+
}
|
|
30820
|
+
}
|
|
30821
|
+
});
|
|
30822
|
+
};
|
|
30784
30823
|
const log = getLogger();
|
|
30785
30824
|
const authHeaders = await resolvePortalAuthHeaders(options, "memory/reflect");
|
|
30786
30825
|
if (authHeaders === null) return baseResult;
|
|
30787
|
-
const controller = new AbortController();
|
|
30788
30826
|
const deadline = Date.now() + REQUEST_TIMEOUT_MS;
|
|
30789
30827
|
const remaining = () => Math.max(0, deadline - Date.now());
|
|
30790
|
-
const
|
|
30791
|
-
|
|
30792
|
-
|
|
30793
|
-
|
|
30794
|
-
|
|
30795
|
-
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30800
|
-
|
|
30801
|
-
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
|
|
30806
|
-
|
|
30807
|
-
|
|
30808
|
-
|
|
30809
|
-
|
|
30810
|
-
|
|
30811
|
-
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
|
|
30815
|
-
|
|
30816
|
-
}
|
|
30817
|
-
}
|
|
30818
|
-
}),
|
|
30819
|
-
signal: controller.signal
|
|
30820
|
-
});
|
|
30821
|
-
} catch (err) {
|
|
30828
|
+
const endpoint = useResponsesTransport ? "/api/v1/responses" : "/api/v1/chat/completions";
|
|
30829
|
+
const sendOnce = async (useResponseFormat) => {
|
|
30830
|
+
const controller = new AbortController();
|
|
30831
|
+
const timer = setTimeout(() => controller.abort(), remaining());
|
|
30832
|
+
let response;
|
|
30833
|
+
try {
|
|
30834
|
+
response = await fetchImpl(`${baseUrl}${endpoint}`, {
|
|
30835
|
+
method: "POST",
|
|
30836
|
+
// No task type unless the caller named one — an undeclared task adds no
|
|
30837
|
+
// header at all, which is what keeps user-facing reflect() unlabelled.
|
|
30838
|
+
headers: {
|
|
30839
|
+
...authHeaders,
|
|
30840
|
+
...taskTypeHeader(options.taskType),
|
|
30841
|
+
"Content-Type": "application/json"
|
|
30842
|
+
},
|
|
30843
|
+
body: buildBody(useResponseFormat),
|
|
30844
|
+
signal: controller.signal
|
|
30845
|
+
});
|
|
30846
|
+
} catch (err) {
|
|
30847
|
+
clearTimeout(timer);
|
|
30848
|
+
log.warn("[memory/reflect] portal request failed", {
|
|
30849
|
+
err: err instanceof Error ? err.message : String(err),
|
|
30850
|
+
baseUrl
|
|
30851
|
+
});
|
|
30852
|
+
return { kind: "error" };
|
|
30853
|
+
}
|
|
30822
30854
|
clearTimeout(timer);
|
|
30823
|
-
|
|
30824
|
-
|
|
30825
|
-
|
|
30855
|
+
if (!response.ok) {
|
|
30856
|
+
return { kind: "http", status: response.status, statusText: response.statusText };
|
|
30857
|
+
}
|
|
30858
|
+
const bodyTimer = setTimeout(() => controller.abort(), remaining());
|
|
30859
|
+
try {
|
|
30860
|
+
const body = await response.json();
|
|
30861
|
+
clearTimeout(bodyTimer);
|
|
30862
|
+
return { kind: "ok", body };
|
|
30863
|
+
} catch (err) {
|
|
30864
|
+
clearTimeout(bodyTimer);
|
|
30865
|
+
log.warn("[memory/reflect] failed to parse portal response body", {
|
|
30866
|
+
err: err instanceof Error ? err.message : String(err)
|
|
30867
|
+
});
|
|
30868
|
+
return { kind: "error" };
|
|
30869
|
+
}
|
|
30870
|
+
};
|
|
30871
|
+
let attempt = await sendOnce(sendResponseFormat);
|
|
30872
|
+
let schemaFallbackUsed = false;
|
|
30873
|
+
const budgetLeftMs = remaining();
|
|
30874
|
+
if (attempt.kind === "http" && sendResponseFormat && !SCHEMA_FALLBACK_SKIP_STATUSES.has(attempt.status) && budgetLeftMs >= MIN_RETRY_BUDGET_MS) {
|
|
30875
|
+
log.warn("[memory/reflect] response_format json_schema rejected; retrying schema-in-prompt", {
|
|
30876
|
+
status: attempt.status,
|
|
30877
|
+
statusText: attempt.statusText,
|
|
30878
|
+
model,
|
|
30879
|
+
taskType: options.taskType,
|
|
30880
|
+
budgetLeftMs
|
|
30826
30881
|
});
|
|
30827
|
-
|
|
30882
|
+
schemaFallbackUsed = true;
|
|
30883
|
+
attempt = await sendOnce(false);
|
|
30828
30884
|
}
|
|
30829
|
-
|
|
30830
|
-
if (
|
|
30885
|
+
if (attempt.kind === "error") return baseResult;
|
|
30886
|
+
if (attempt.kind === "http") {
|
|
30831
30887
|
log.warn("[memory/reflect] portal returned non-OK", {
|
|
30832
|
-
status:
|
|
30833
|
-
statusText:
|
|
30834
|
-
|
|
30835
|
-
|
|
30836
|
-
}
|
|
30837
|
-
const bodyTimer = setTimeout(() => controller.abort(), remaining());
|
|
30838
|
-
let body;
|
|
30839
|
-
try {
|
|
30840
|
-
body = await response.json();
|
|
30841
|
-
} catch (err) {
|
|
30842
|
-
clearTimeout(bodyTimer);
|
|
30843
|
-
log.warn("[memory/reflect] failed to parse portal response body", {
|
|
30844
|
-
err: err instanceof Error ? err.message : String(err)
|
|
30888
|
+
status: attempt.status,
|
|
30889
|
+
statusText: attempt.statusText,
|
|
30890
|
+
// Distinguishes "the fallback ran and still failed" from "never tried".
|
|
30891
|
+
schemaFallbackUsed
|
|
30845
30892
|
});
|
|
30846
30893
|
return baseResult;
|
|
30847
30894
|
}
|
|
30848
|
-
|
|
30849
|
-
return parseAnswer(body, baseResult, !!options.responseSchema);
|
|
30895
|
+
return parseAnswer(attempt.body, baseResult, !!options.responseSchema);
|
|
30850
30896
|
}
|
|
30851
30897
|
function parseAnswer(body, base, parseSchema) {
|
|
30852
30898
|
if (typeof body !== "object" || body === null) return base;
|
|
30853
30899
|
const obj = body;
|
|
30854
|
-
const
|
|
30855
|
-
let text12 = "";
|
|
30856
|
-
if (Array.isArray(choices) && choices.length > 0) {
|
|
30857
|
-
const message = choices[0].message;
|
|
30858
|
-
if (typeof message === "object" && message !== null) {
|
|
30859
|
-
const content = message.content;
|
|
30860
|
-
if (typeof content === "string") text12 = content;
|
|
30861
|
-
}
|
|
30862
|
-
}
|
|
30900
|
+
const text12 = extractCompletionContent(obj) ?? "";
|
|
30863
30901
|
const usage = obj.usage;
|
|
30864
30902
|
let structuredOutput;
|
|
30865
30903
|
if (parseSchema && text12) {
|
|
@@ -30871,14 +30909,19 @@ function parseAnswer(body, base, parseSchema) {
|
|
|
30871
30909
|
});
|
|
30872
30910
|
}
|
|
30873
30911
|
}
|
|
30912
|
+
const promptTokens = usage?.prompt_tokens ?? usage?.input_tokens ?? 0;
|
|
30913
|
+
const completionTokens = usage?.completion_tokens ?? usage?.output_tokens ?? 0;
|
|
30874
30914
|
return {
|
|
30875
30915
|
text: text12,
|
|
30876
30916
|
...structuredOutput !== void 0 && { structuredOutput },
|
|
30877
30917
|
basedOn: base.basedOn,
|
|
30878
30918
|
usage: {
|
|
30879
|
-
promptTokens
|
|
30880
|
-
completionTokens
|
|
30881
|
-
|
|
30919
|
+
promptTokens,
|
|
30920
|
+
completionTokens,
|
|
30921
|
+
// Derived when absent: the Responses API reports the two component counts
|
|
30922
|
+
// and does not always send a total, which would otherwise report zero
|
|
30923
|
+
// spend for a call that plainly cost something.
|
|
30924
|
+
totalTokens: usage?.total_tokens ?? promptTokens + completionTokens
|
|
30882
30925
|
}
|
|
30883
30926
|
};
|
|
30884
30927
|
}
|
package/dist/server/index.mjs
CHANGED
|
@@ -141,23 +141,25 @@ import {
|
|
|
141
141
|
updateVaultMemoryOp,
|
|
142
142
|
upsertMessageOp,
|
|
143
143
|
vaultMemoryToStored
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-ARYSRI4Z.mjs";
|
|
145
145
|
import {
|
|
146
146
|
createStreamingDeAnonymizer,
|
|
147
147
|
notifyConsolidationFallback,
|
|
148
148
|
resolvePiiRedactor
|
|
149
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-4LPSEROP.mjs";
|
|
150
150
|
import {
|
|
151
151
|
INTERNAL_FLOW_MARKER,
|
|
152
152
|
callPortalJsonCompletion,
|
|
153
|
+
extractCompletionContent,
|
|
153
154
|
extractJsonCandidate,
|
|
154
155
|
getLogger,
|
|
156
|
+
requiresResponsesTransport,
|
|
155
157
|
resolvePortalAuthHeaders,
|
|
156
158
|
supportsResponseFormat,
|
|
157
159
|
taskTypeHeader,
|
|
158
160
|
validateEndpointOverride,
|
|
159
161
|
withInternalFlowMarker
|
|
160
|
-
} from "./chunk-
|
|
162
|
+
} from "./chunk-BSCEZOIW.mjs";
|
|
161
163
|
import {
|
|
162
164
|
BASE_URL,
|
|
163
165
|
__commonJS,
|
|
@@ -5700,7 +5702,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
5700
5702
|
content: m.content,
|
|
5701
5703
|
similarity: m.similarity
|
|
5702
5704
|
}));
|
|
5703
|
-
const { consolidateMemory: doConsolidate } = await import("./consolidate-
|
|
5705
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-MSAXASHL.mjs");
|
|
5704
5706
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
5705
5707
|
if (decision.action === "create") return null;
|
|
5706
5708
|
if (decision.action === "supersede" && decision.content) {
|
|
@@ -6924,6 +6926,9 @@ var DEFAULT_BASE_URL = BASE_URL;
|
|
|
6924
6926
|
var DEFAULT_MODEL3 = "anthropic/claude-sonnet-4-6";
|
|
6925
6927
|
var DEFAULT_MAX_TOKENS = 4096;
|
|
6926
6928
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
6929
|
+
var MIN_RETRY_BUDGET_MS = 2e3;
|
|
6930
|
+
var MIN_RESPONSES_OUTPUT_TOKENS = 2048;
|
|
6931
|
+
var SCHEMA_FALLBACK_SKIP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 408, 409, 413, 425, 429]);
|
|
6927
6932
|
var DEFAULT_SYSTEM_PROMPT = `You are a personal assistant with access to the user's memory. Answer the user's question using the supplied memories as evidence.
|
|
6928
6933
|
|
|
6929
6934
|
Rules:
|
|
@@ -6955,14 +6960,9 @@ ${m.content}`).join("\n\n");
|
|
|
6955
6960
|
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
6956
6961
|
const model = options.llmModel ?? DEFAULT_MODEL3;
|
|
6957
6962
|
const wantsStructured = !!options.responseSchema;
|
|
6958
|
-
const
|
|
6963
|
+
const useResponsesTransport = requiresResponsesTransport(model);
|
|
6964
|
+
const sendResponseFormat = wantsStructured && !useResponsesTransport && supportsResponseFormat(model, "json_schema");
|
|
6959
6965
|
const basePrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
6960
|
-
const systemPrompt = wantsStructured && !sendResponseFormat ? `${basePrompt}
|
|
6961
|
-
|
|
6962
|
-
Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
|
|
6963
|
-
${JSON.stringify(
|
|
6964
|
-
options.responseSchema
|
|
6965
|
-
)}` : basePrompt;
|
|
6966
6966
|
const userMessage = [
|
|
6967
6967
|
`Question:
|
|
6968
6968
|
${trimmed}`,
|
|
@@ -6972,85 +6972,121 @@ ${evidence}`
|
|
|
6972
6972
|
].join("\n\n");
|
|
6973
6973
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
6974
6974
|
const fetchImpl = options.fetchFn ?? fetch;
|
|
6975
|
+
const buildBody = (useResponseFormat) => {
|
|
6976
|
+
const systemPrompt = wantsStructured && !useResponseFormat ? `${basePrompt}
|
|
6977
|
+
|
|
6978
|
+
Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
|
|
6979
|
+
${JSON.stringify(
|
|
6980
|
+
options.responseSchema
|
|
6981
|
+
)}` : basePrompt;
|
|
6982
|
+
const messages = [
|
|
6983
|
+
{ role: "system", content: systemPrompt },
|
|
6984
|
+
{ role: "user", content: userMessage }
|
|
6985
|
+
];
|
|
6986
|
+
if (useResponsesTransport) {
|
|
6987
|
+
return JSON.stringify({
|
|
6988
|
+
model,
|
|
6989
|
+
// Same system+user pair under the Responses-API field names: `input`
|
|
6990
|
+
// for the turns, `max_output_tokens` for the cap. Sending the
|
|
6991
|
+
// chat-completions spelling here is silently ignored, which caps the
|
|
6992
|
+
// answer at the portal default.
|
|
6993
|
+
input: messages,
|
|
6994
|
+
max_output_tokens: Math.max(maxTokens, MIN_RESPONSES_OUTPUT_TOKENS)
|
|
6995
|
+
});
|
|
6996
|
+
}
|
|
6997
|
+
return JSON.stringify({
|
|
6998
|
+
model,
|
|
6999
|
+
// Modern OpenAI field; the portal reads only `max_completion_tokens`
|
|
7000
|
+
// (the deprecated `max_tokens` is silently ignored → falls back to the
|
|
7001
|
+
// portal's default output cap and truncates the answer).
|
|
7002
|
+
max_completion_tokens: maxTokens,
|
|
7003
|
+
messages,
|
|
7004
|
+
...useResponseFormat && {
|
|
7005
|
+
response_format: {
|
|
7006
|
+
type: "json_schema",
|
|
7007
|
+
json_schema: { name: "reflect_output", schema: options.responseSchema }
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
});
|
|
7011
|
+
};
|
|
6975
7012
|
const log = getLogger();
|
|
6976
7013
|
const authHeaders = await resolvePortalAuthHeaders(options, "memory/reflect");
|
|
6977
7014
|
if (authHeaders === null) return baseResult;
|
|
6978
|
-
const controller = new AbortController();
|
|
6979
7015
|
const deadline = Date.now() + REQUEST_TIMEOUT_MS;
|
|
6980
7016
|
const remaining = () => Math.max(0, deadline - Date.now());
|
|
6981
|
-
const
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
}
|
|
7008
|
-
}
|
|
7009
|
-
}),
|
|
7010
|
-
signal: controller.signal
|
|
7011
|
-
});
|
|
7012
|
-
} catch (err) {
|
|
7017
|
+
const endpoint = useResponsesTransport ? "/api/v1/responses" : "/api/v1/chat/completions";
|
|
7018
|
+
const sendOnce = async (useResponseFormat) => {
|
|
7019
|
+
const controller = new AbortController();
|
|
7020
|
+
const timer = setTimeout(() => controller.abort(), remaining());
|
|
7021
|
+
let response;
|
|
7022
|
+
try {
|
|
7023
|
+
response = await fetchImpl(`${baseUrl}${endpoint}`, {
|
|
7024
|
+
method: "POST",
|
|
7025
|
+
// No task type unless the caller named one — an undeclared task adds no
|
|
7026
|
+
// header at all, which is what keeps user-facing reflect() unlabelled.
|
|
7027
|
+
headers: {
|
|
7028
|
+
...authHeaders,
|
|
7029
|
+
...taskTypeHeader(options.taskType),
|
|
7030
|
+
"Content-Type": "application/json"
|
|
7031
|
+
},
|
|
7032
|
+
body: buildBody(useResponseFormat),
|
|
7033
|
+
signal: controller.signal
|
|
7034
|
+
});
|
|
7035
|
+
} catch (err) {
|
|
7036
|
+
clearTimeout(timer);
|
|
7037
|
+
log.warn("[memory/reflect] portal request failed", {
|
|
7038
|
+
err: err instanceof Error ? err.message : String(err),
|
|
7039
|
+
baseUrl
|
|
7040
|
+
});
|
|
7041
|
+
return { kind: "error" };
|
|
7042
|
+
}
|
|
7013
7043
|
clearTimeout(timer);
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7044
|
+
if (!response.ok) {
|
|
7045
|
+
return { kind: "http", status: response.status, statusText: response.statusText };
|
|
7046
|
+
}
|
|
7047
|
+
const bodyTimer = setTimeout(() => controller.abort(), remaining());
|
|
7048
|
+
try {
|
|
7049
|
+
const body = await response.json();
|
|
7050
|
+
clearTimeout(bodyTimer);
|
|
7051
|
+
return { kind: "ok", body };
|
|
7052
|
+
} catch (err) {
|
|
7053
|
+
clearTimeout(bodyTimer);
|
|
7054
|
+
log.warn("[memory/reflect] failed to parse portal response body", {
|
|
7055
|
+
err: err instanceof Error ? err.message : String(err)
|
|
7056
|
+
});
|
|
7057
|
+
return { kind: "error" };
|
|
7058
|
+
}
|
|
7059
|
+
};
|
|
7060
|
+
let attempt = await sendOnce(sendResponseFormat);
|
|
7061
|
+
let schemaFallbackUsed = false;
|
|
7062
|
+
const budgetLeftMs = remaining();
|
|
7063
|
+
if (attempt.kind === "http" && sendResponseFormat && !SCHEMA_FALLBACK_SKIP_STATUSES.has(attempt.status) && budgetLeftMs >= MIN_RETRY_BUDGET_MS) {
|
|
7064
|
+
log.warn("[memory/reflect] response_format json_schema rejected; retrying schema-in-prompt", {
|
|
7065
|
+
status: attempt.status,
|
|
7066
|
+
statusText: attempt.statusText,
|
|
7067
|
+
model,
|
|
7068
|
+
taskType: options.taskType,
|
|
7069
|
+
budgetLeftMs
|
|
7017
7070
|
});
|
|
7018
|
-
|
|
7071
|
+
schemaFallbackUsed = true;
|
|
7072
|
+
attempt = await sendOnce(false);
|
|
7019
7073
|
}
|
|
7020
|
-
|
|
7021
|
-
if (
|
|
7074
|
+
if (attempt.kind === "error") return baseResult;
|
|
7075
|
+
if (attempt.kind === "http") {
|
|
7022
7076
|
log.warn("[memory/reflect] portal returned non-OK", {
|
|
7023
|
-
status:
|
|
7024
|
-
statusText:
|
|
7077
|
+
status: attempt.status,
|
|
7078
|
+
statusText: attempt.statusText,
|
|
7079
|
+
// Distinguishes "the fallback ran and still failed" from "never tried".
|
|
7080
|
+
schemaFallbackUsed
|
|
7025
7081
|
});
|
|
7026
7082
|
return baseResult;
|
|
7027
7083
|
}
|
|
7028
|
-
|
|
7029
|
-
let body;
|
|
7030
|
-
try {
|
|
7031
|
-
body = await response.json();
|
|
7032
|
-
} catch (err) {
|
|
7033
|
-
clearTimeout(bodyTimer);
|
|
7034
|
-
log.warn("[memory/reflect] failed to parse portal response body", {
|
|
7035
|
-
err: err instanceof Error ? err.message : String(err)
|
|
7036
|
-
});
|
|
7037
|
-
return baseResult;
|
|
7038
|
-
}
|
|
7039
|
-
clearTimeout(bodyTimer);
|
|
7040
|
-
return parseAnswer(body, baseResult, !!options.responseSchema);
|
|
7084
|
+
return parseAnswer(attempt.body, baseResult, !!options.responseSchema);
|
|
7041
7085
|
}
|
|
7042
7086
|
function parseAnswer(body, base, parseSchema) {
|
|
7043
7087
|
if (typeof body !== "object" || body === null) return base;
|
|
7044
7088
|
const obj = body;
|
|
7045
|
-
const
|
|
7046
|
-
let text12 = "";
|
|
7047
|
-
if (Array.isArray(choices) && choices.length > 0) {
|
|
7048
|
-
const message = choices[0].message;
|
|
7049
|
-
if (typeof message === "object" && message !== null) {
|
|
7050
|
-
const content = message.content;
|
|
7051
|
-
if (typeof content === "string") text12 = content;
|
|
7052
|
-
}
|
|
7053
|
-
}
|
|
7089
|
+
const text12 = extractCompletionContent(obj) ?? "";
|
|
7054
7090
|
const usage = obj.usage;
|
|
7055
7091
|
let structuredOutput;
|
|
7056
7092
|
if (parseSchema && text12) {
|
|
@@ -7062,14 +7098,19 @@ function parseAnswer(body, base, parseSchema) {
|
|
|
7062
7098
|
});
|
|
7063
7099
|
}
|
|
7064
7100
|
}
|
|
7101
|
+
const promptTokens = usage?.prompt_tokens ?? usage?.input_tokens ?? 0;
|
|
7102
|
+
const completionTokens = usage?.completion_tokens ?? usage?.output_tokens ?? 0;
|
|
7065
7103
|
return {
|
|
7066
7104
|
text: text12,
|
|
7067
7105
|
...structuredOutput !== void 0 && { structuredOutput },
|
|
7068
7106
|
basedOn: base.basedOn,
|
|
7069
7107
|
usage: {
|
|
7070
|
-
promptTokens
|
|
7071
|
-
completionTokens
|
|
7072
|
-
|
|
7108
|
+
promptTokens,
|
|
7109
|
+
completionTokens,
|
|
7110
|
+
// Derived when absent: the Responses API reports the two component counts
|
|
7111
|
+
// and does not always send a total, which would otherwise report zero
|
|
7112
|
+
// spend for a call that plainly cost something.
|
|
7113
|
+
totalTokens: usage?.total_tokens ?? promptTokens + completionTokens
|
|
7073
7114
|
}
|
|
7074
7115
|
};
|
|
7075
7116
|
}
|