@wudao-org/providers 0.0.0-stage → 0.4.21
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/LICENSE +21 -0
- package/README.md +8 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/anthropic.d.ts +19 -0
- package/dist/anthropic.d.ts.map +1 -0
- package/dist/anthropic.js +621 -0
- package/dist/anthropic.js.map +1 -0
- package/dist/balance.d.ts +28 -0
- package/dist/balance.d.ts.map +1 -0
- package/dist/balance.js +105 -0
- package/dist/balance.js.map +1 -0
- package/dist/config.d.ts +328 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +623 -0
- package/dist/config.js.map +1 -0
- package/dist/dispatcher.d.ts +5 -0
- package/dist/dispatcher.d.ts.map +1 -0
- package/dist/dispatcher.js +21 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/effort-catalog.d.ts +17 -0
- package/dist/effort-catalog.d.ts.map +1 -0
- package/dist/effort-catalog.js +73 -0
- package/dist/effort-catalog.js.map +1 -0
- package/dist/error-body.d.ts +32 -0
- package/dist/error-body.d.ts.map +1 -0
- package/dist/error-body.js +74 -0
- package/dist/error-body.js.map +1 -0
- package/dist/header-validation.d.ts +57 -0
- package/dist/header-validation.d.ts.map +1 -0
- package/dist/header-validation.js +130 -0
- package/dist/header-validation.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/json-arguments.d.ts +17 -0
- package/dist/json-arguments.d.ts.map +1 -0
- package/dist/json-arguments.js +227 -0
- package/dist/json-arguments.js.map +1 -0
- package/dist/key-pool.d.ts +66 -0
- package/dist/key-pool.d.ts.map +1 -0
- package/dist/key-pool.js +207 -0
- package/dist/key-pool.js.map +1 -0
- package/dist/missing-reasoning-circuit.d.ts +17 -0
- package/dist/missing-reasoning-circuit.d.ts.map +1 -0
- package/dist/missing-reasoning-circuit.js +38 -0
- package/dist/missing-reasoning-circuit.js.map +1 -0
- package/dist/model-failure.d.ts +156 -0
- package/dist/model-failure.d.ts.map +1 -0
- package/dist/model-failure.js +253 -0
- package/dist/model-failure.js.map +1 -0
- package/dist/model-fallback.d.ts +18 -0
- package/dist/model-fallback.d.ts.map +1 -0
- package/dist/model-fallback.js +87 -0
- package/dist/model-fallback.js.map +1 -0
- package/dist/models.d.ts +20 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +55 -0
- package/dist/models.js.map +1 -0
- package/dist/openai-compatible.d.ts +87 -0
- package/dist/openai-compatible.d.ts.map +1 -0
- package/dist/openai-compatible.js +368 -0
- package/dist/openai-compatible.js.map +1 -0
- package/dist/openai-engine.d.ts +18 -0
- package/dist/openai-engine.d.ts.map +1 -0
- package/dist/openai-engine.js +515 -0
- package/dist/openai-engine.js.map +1 -0
- package/dist/openai-images.d.ts +38 -0
- package/dist/openai-images.d.ts.map +1 -0
- package/dist/openai-images.js +301 -0
- package/dist/openai-images.js.map +1 -0
- package/dist/openai-messages.d.ts +40 -0
- package/dist/openai-messages.d.ts.map +1 -0
- package/dist/openai-messages.js +135 -0
- package/dist/openai-messages.js.map +1 -0
- package/dist/openai-wire.d.ts +93 -0
- package/dist/openai-wire.d.ts.map +1 -0
- package/dist/openai-wire.js +151 -0
- package/dist/openai-wire.js.map +1 -0
- package/dist/persona/apply.d.ts +19 -0
- package/dist/persona/apply.d.ts.map +1 -0
- package/dist/persona/apply.js +98 -0
- package/dist/persona/apply.js.map +1 -0
- package/dist/persona/builtins.d.ts +3 -0
- package/dist/persona/builtins.d.ts.map +1 -0
- package/dist/persona/builtins.js +180 -0
- package/dist/persona/builtins.js.map +1 -0
- package/dist/persona/decorator.d.ts +4 -0
- package/dist/persona/decorator.d.ts.map +1 -0
- package/dist/persona/decorator.js +112 -0
- package/dist/persona/decorator.js.map +1 -0
- package/dist/persona/files.d.ts +20 -0
- package/dist/persona/files.d.ts.map +1 -0
- package/dist/persona/files.js +84 -0
- package/dist/persona/files.js.map +1 -0
- package/dist/persona/ids.d.ts +9 -0
- package/dist/persona/ids.d.ts.map +1 -0
- package/dist/persona/ids.js +11 -0
- package/dist/persona/ids.js.map +1 -0
- package/dist/persona/registry.d.ts +12 -0
- package/dist/persona/registry.d.ts.map +1 -0
- package/dist/persona/registry.js +54 -0
- package/dist/persona/registry.js.map +1 -0
- package/dist/persona/runtime.d.ts +23 -0
- package/dist/persona/runtime.d.ts.map +1 -0
- package/dist/persona/runtime.js +32 -0
- package/dist/persona/runtime.js.map +1 -0
- package/dist/persona/schema.d.ts +27 -0
- package/dist/persona/schema.d.ts.map +1 -0
- package/dist/persona/schema.js +165 -0
- package/dist/persona/schema.js.map +1 -0
- package/dist/persona/types.d.ts +38 -0
- package/dist/persona/types.d.ts.map +1 -0
- package/dist/persona/types.js +2 -0
- package/dist/persona/types.js.map +1 -0
- package/dist/pricing.d.ts +40 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +70 -0
- package/dist/pricing.js.map +1 -0
- package/dist/quota-park.d.ts +53 -0
- package/dist/quota-park.d.ts.map +1 -0
- package/dist/quota-park.js +135 -0
- package/dist/quota-park.js.map +1 -0
- package/dist/rate-pause.d.ts +28 -0
- package/dist/rate-pause.d.ts.map +1 -0
- package/dist/rate-pause.js +72 -0
- package/dist/rate-pause.js.map +1 -0
- package/dist/registry.d.ts +142 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +471 -0
- package/dist/registry.js.map +1 -0
- package/dist/responses.d.ts +5 -0
- package/dist/responses.d.ts.map +1 -0
- package/dist/responses.js +290 -0
- package/dist/responses.js.map +1 -0
- package/dist/retry-after.d.ts +13 -0
- package/dist/retry-after.d.ts.map +1 -0
- package/dist/retry-after.js +64 -0
- package/dist/retry-after.js.map +1 -0
- package/dist/retry-ladder.d.ts +176 -0
- package/dist/retry-ladder.d.ts.map +1 -0
- package/dist/retry-ladder.js +664 -0
- package/dist/retry-ladder.js.map +1 -0
- package/dist/split-inband-thinking.d.ts +35 -0
- package/dist/split-inband-thinking.d.ts.map +1 -0
- package/dist/split-inband-thinking.js +117 -0
- package/dist/split-inband-thinking.js.map +1 -0
- package/dist/stream-idle.d.ts +97 -0
- package/dist/stream-idle.d.ts.map +1 -0
- package/dist/stream-idle.js +127 -0
- package/dist/stream-idle.js.map +1 -0
- package/dist/tool-call-ids.d.ts +16 -0
- package/dist/tool-call-ids.d.ts.map +1 -0
- package/dist/tool-call-ids.js +36 -0
- package/dist/tool-call-ids.js.map +1 -0
- package/package.json +32 -3
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// 拆分运动 C:从 openai-compatible.ts 原样外提的 M79 §2 图片请求域
|
|
2
|
+
//(AttachmentResolver/ImageRequestContext、keep-K 窗口、imageWireMessages 走查)。
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { UnsupportedContentError, imageOffloadPlaceholder, offloadRequestImages, } from "@wudao-org/core";
|
|
5
|
+
import { resolveInputModalities } from "./models.js";
|
|
6
|
+
import { restoreToolCallId } from "./tool-call-ids.js";
|
|
7
|
+
import { toOpenAIMessage } from "./openai-messages.js";
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// M79 §2 — image-bearing requests (dsh parity; research 40 §1).
|
|
10
|
+
//
|
|
11
|
+
// gate images need a model that DECLARES the image input modality and
|
|
12
|
+
// an assembled attachment resolver; assistant history carrying
|
|
13
|
+
// images is structurally refused. All three fail with the typed
|
|
14
|
+
// UnsupportedContentError BEFORE any network call (and before a
|
|
15
|
+
// single image byte is read).
|
|
16
|
+
// shape a user turn with images → OpenAI-compatible content parts
|
|
17
|
+
// (`image_url` carrying the TRANSIENT data URL); a pure-text
|
|
18
|
+
// user turn keeps the compact string form, byte-identical to
|
|
19
|
+
// pre-M79.
|
|
20
|
+
// tool wire the `tool` role stays string-only: images riding tool results
|
|
21
|
+
// buffer into ONE synthetic user message immediately following
|
|
22
|
+
// the tool block, prefixed "Attached image(s) from tool result:".
|
|
23
|
+
// offload over the cumulative base64 budget, the OLDEST images become
|
|
24
|
+
// fixed text placeholders (deterministic); omitted bytes are
|
|
25
|
+
// never resolved.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/** Prefix of the synthetic user message that carries tool-result images. */
|
|
28
|
+
export const TOOL_IMAGE_NOTE = "Attached image(s) from tool result:";
|
|
29
|
+
/** ω99-138 keep-K — how many TOOL-RESULT images ride a request as image_url
|
|
30
|
+
* parts. The M79 byte budget (20MiB) bounds BYTES, not visual tokens: a long
|
|
31
|
+
* browser session's screenshots would all decode to image_url parts on EVERY
|
|
32
|
+
* request (40~200 images = the death spiral 135's risk list put first), so
|
|
33
|
+
* the request projection keeps a sliding window of the most recent K and
|
|
34
|
+
* stubs everything older with the FIXED text below. USER images are NOT
|
|
35
|
+
* windowed (zero drift — they keep their full M79 behavior).
|
|
36
|
+
*
|
|
37
|
+
* ω99-244: #3 — K 2→3 (computer arc C's screenshot-history stub, brief
|
|
38
|
+
* N=3): the SAME request-build projection seam — turn-boundary timing
|
|
39
|
+
* unchanged (projection happens between turns, at request build, never
|
|
40
|
+
* mid-flight), window-slide semantics unchanged, only one slot wider.
|
|
41
|
+
* M24 cache cost (declared, not hidden): widening K flips the 3rd-newest
|
|
42
|
+
* tool image from stub to image_url for sessions spanning the upgrade —
|
|
43
|
+
* ONE cross-version cold turn (cold turn expected), never inside a session;
|
|
44
|
+
* the stub text below changes bytes in the same version, same single break;
|
|
45
|
+
* the M24 compensation narrative in imageWireMessages covers both. Within a
|
|
46
|
+
* session the window self-heals every request build (a stubbed image that
|
|
47
|
+
* re-enters the window renders again — no compaction-timing cliff). */
|
|
48
|
+
const TOOL_IMAGE_KEEP_K = 3;
|
|
49
|
+
/** The fixed stub for window-evicted tool images — literal-pinned by tests
|
|
50
|
+
* (deterministic bytes: two builds of the same history project identically).
|
|
51
|
+
* ω99-244: derived from K so the two can never drift apart. */
|
|
52
|
+
const TOOL_IMAGE_KEEP_K_STUB = `[更早的截图已略:仅保留最近 ${TOOL_IMAGE_KEEP_K} 张]`;
|
|
53
|
+
/** The content a tool message shows when its bytes went to the following
|
|
54
|
+
* synthetic user message (dsh wire shape). */
|
|
55
|
+
const TOOL_IMAGE_CONTENT_FALLBACK = "(see attached image)";
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// 借鉴弧 β1 — adjacent byte-dedup inside the keep-K window (hermes
|
|
58
|
+
// 682b973b32, digest-b C2). The incumbent window bounds COUNT, not
|
|
59
|
+
// REDUNDANCE: a static capture→act→capture loop fills all K slots with
|
|
60
|
+
// byte-identical frames and burns K× the visual tokens for ONE screen.
|
|
61
|
+
//
|
|
62
|
+
// Dedup lives at THIS request-build projection seam only — the same seam
|
|
63
|
+
// ω99-138/244 established (turn-boundary timing, per-request self-healing,
|
|
64
|
+
// persisted history untouched). The window members' transient data URLs are
|
|
65
|
+
// resolved once and hashed (sha256 over the full `data:<mime>;base64,…`
|
|
66
|
+
// string ≡ sha256(mime+bytes) — hermes's exact dedup key); a newest→oldest
|
|
67
|
+
// walk keeps the NEWEST frame of every identical-ADJACENT run and replaces
|
|
68
|
+
// older duplicates with the FIXED note below. USER images never take the
|
|
69
|
+
// note (zero drift, same as the window).
|
|
70
|
+
//
|
|
71
|
+
// M24 cache cost (declared, not hidden): the projection stays append-only —
|
|
72
|
+
// every ref remains in the JSONL, the note bytes are FIXED, so two builds of
|
|
73
|
+
// the same history project identically. When an adjacent duplicate lands,
|
|
74
|
+
// the previous anchor flips image_url → note: ONE additional deterministic
|
|
75
|
+
// flip at the same turn-boundary timing as the window slide (never
|
|
76
|
+
// mid-flight) — same cold-turn-expected class ω99-244 declared for K; in a
|
|
77
|
+
// static loop the request carries ONE image instead of K identical ones, so
|
|
78
|
+
// visual tokens strictly fall while request bytes get MORE stable (the
|
|
79
|
+
// multi-MiB duplicated blobs stop riding).
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/** β1 — max consecutive adjacent-duplicate frames rendered as the note
|
|
82
|
+
* before a re-anchor image is forced (hermes's 省略连击上限: staleness guard
|
|
83
|
+
* — the kept frame can never drift arbitrarily far from the newest state,
|
|
84
|
+
* and a compaction/window eviction of the anchor degrades at most cap
|
|
85
|
+
* notes, not an unbounded run). At K=3 the cap admits the two older slots
|
|
86
|
+
* of a fully static window as notes; K widened later re-anchors at every
|
|
87
|
+
* cap+1 frames automatically (the walk is K-agnostic). */
|
|
88
|
+
const TOOL_IMAGE_DEDUP_OMIT_CAP = 2;
|
|
89
|
+
/** β1 — the fixed note replacing an adjacent-duplicate frame (literal-pinned
|
|
90
|
+
* by tests; deterministic bytes: same history ⇒ same projection). */
|
|
91
|
+
const TOOL_IMAGE_DEDUP_STUB = "[截图未变:本帧与相邻截图逐字节相同,已省略图片——保留帧即当前屏幕状态]";
|
|
92
|
+
/** β1 — the pure newest→oldest dedup walk over one window's content hashes
|
|
93
|
+
* (input in HISTORY order, oldest→newest). Returns per-frame verdicts:
|
|
94
|
+
* true = renders as image_url, false = the fixed note. A frame is omitted
|
|
95
|
+
* only when its hash equals its immediate NEWER neighbor's AND the
|
|
96
|
+
* omission streak stays under `omitCap`. Exported so the policy itself is
|
|
97
|
+
* unit-testable at any window width (the seam below only feeds it hashes
|
|
98
|
+
* — at K=3 the re-anchor branch needs a 4-wide window to show, which only
|
|
99
|
+
* the direct test can see). */
|
|
100
|
+
export function sameImageKeepVerdicts(hashes, omitCap) {
|
|
101
|
+
const verdicts = new Array(hashes.length);
|
|
102
|
+
let streak = 0;
|
|
103
|
+
for (let i = hashes.length - 1; i >= 0; i -= 1) {
|
|
104
|
+
const newer = hashes[i + 1];
|
|
105
|
+
if (newer !== undefined && hashes[i] === newer && streak < omitCap) {
|
|
106
|
+
verdicts[i] = false;
|
|
107
|
+
streak += 1;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
verdicts[i] = true;
|
|
111
|
+
streak = 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return verdicts;
|
|
115
|
+
}
|
|
116
|
+
/** Does any message in the input carry image blocks? (Cheap pre-filter —
|
|
117
|
+
* the no-image path below is the byte-identical pre-M79 construction.) */
|
|
118
|
+
export function inputHasImages(input) {
|
|
119
|
+
return input.messages.some((m) => {
|
|
120
|
+
const imgs = m.images;
|
|
121
|
+
return Array.isArray(imgs) && imgs.length > 0;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** The pure pre-I/O capability/role gate. Throws the typed
|
|
125
|
+
* UnsupportedContentError — never retries (see isRetryableProviderError) —
|
|
126
|
+
* for (a) images addressed at a model without the `image` modality, and
|
|
127
|
+
* (b) ImageBlocks in assistant history (only user turns and tool results
|
|
128
|
+
* may carry images). Exported so the CLI can pre-check a submit. */
|
|
129
|
+
export function assertImagesSupported(input, modalities) {
|
|
130
|
+
let has = false;
|
|
131
|
+
for (const m of input.messages) {
|
|
132
|
+
const imgs = m.images;
|
|
133
|
+
if (!Array.isArray(imgs) || imgs.length === 0)
|
|
134
|
+
continue;
|
|
135
|
+
has = true;
|
|
136
|
+
if (m.role === "assistant") {
|
|
137
|
+
throw new UnsupportedContentError("image-in-unsupported-role", "images may ride user turns and tool results only — assistant history carries an ImageBlock");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (has && !modalities.includes("image")) {
|
|
141
|
+
throw new UnsupportedContentError("model-no-image-modality", `model does not declare the image input modality (inputModalities: [${modalities.join(", ")}]) — refusing the image-bearing request`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** All image blocks of a request, in message order (oldest first). */
|
|
145
|
+
function collectRequestImages(input) {
|
|
146
|
+
const out = [];
|
|
147
|
+
for (const m of input.messages) {
|
|
148
|
+
const imgs = m.images;
|
|
149
|
+
if (Array.isArray(imgs) && imgs.length > 0)
|
|
150
|
+
out.push(...imgs);
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** The image-bearing message walk (system + per-message shaping + the
|
|
155
|
+
* synthetic tool-image user message). Only called when inputHasImages —
|
|
156
|
+
* the text-only path is toOpenAIRequest's, byte-identical. */
|
|
157
|
+
export async function imageWireMessages(config, input, imageCtx) {
|
|
158
|
+
// Gate BEFORE reading a single image byte (capability + role).
|
|
159
|
+
assertImagesSupported(input, resolveInputModalities(config));
|
|
160
|
+
if (imageCtx.resolver === undefined) {
|
|
161
|
+
throw new UnsupportedContentError("no-attachment-store", "no attachment resolver is assembled on this provider — image-bearing input cannot be built (images are never silently dropped)");
|
|
162
|
+
}
|
|
163
|
+
// Offload plan from the DURABLE refs' sizes (pure); only the survivors'
|
|
164
|
+
// bytes are ever resolved.
|
|
165
|
+
//
|
|
166
|
+
// ω99-138 keep-K window (M24: compensation ships BEFORE the feature).
|
|
167
|
+
// TOOL-result images beyond the most recent K drop out of the projection
|
|
168
|
+
// here — BEFORE the byte-budget offload plan — so an evicted image neither
|
|
169
|
+
// resolves bytes nor spends budget; on the wire its part is the fixed stub
|
|
170
|
+
// above (the carrier message keeps one part per image, position-stable).
|
|
171
|
+
// CACHE COST (M24 format): the window lives ONLY in this request build —
|
|
172
|
+
// persisted history keeps every ref (append-only, never rewritten). When
|
|
173
|
+
// the window slides (the K+1-st tool image lands), the oldest window
|
|
174
|
+
// member flips from image_url to the stub: the synthetic carrier's bytes
|
|
175
|
+
// change at that point and break the prefix ONCE there = cold turn
|
|
176
|
+
// expected. Turn-boundary timing holds by construction (projection happens
|
|
177
|
+
// between turns, at request build, never mid-flight).
|
|
178
|
+
const toolImageIdsInOrder = [];
|
|
179
|
+
const toolImageIds = new Set();
|
|
180
|
+
const toolImageRefById = new Map();
|
|
181
|
+
for (const m of input.messages) {
|
|
182
|
+
if (m.role !== "tool")
|
|
183
|
+
continue;
|
|
184
|
+
const imgs = m.images;
|
|
185
|
+
if (!Array.isArray(imgs))
|
|
186
|
+
continue;
|
|
187
|
+
for (const b of imgs) {
|
|
188
|
+
if (!toolImageIds.has(b.ref.attachmentId)) {
|
|
189
|
+
toolImageIds.add(b.ref.attachmentId);
|
|
190
|
+
toolImageIdsInOrder.push(b.ref.attachmentId);
|
|
191
|
+
toolImageRefById.set(b.ref.attachmentId, b.ref);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const keepKIds = new Set(toolImageIdsInOrder.slice(-TOOL_IMAGE_KEEP_K));
|
|
196
|
+
const isWindowStubbed = (b) => toolImageIds.has(b.ref.attachmentId) && !keepKIds.has(b.ref.attachmentId);
|
|
197
|
+
// β1 byte-dedup (see the block comment above the constants): resolve each
|
|
198
|
+
// WINDOW member once — the resolution doubles as the content hash source —
|
|
199
|
+
// then let the pure newest→oldest walk decide which frames still render.
|
|
200
|
+
// Hashing is an OPTIMIZATION seam and fails OPEN here (an unresolvable
|
|
201
|
+
// frame simply never dedups): the render path below keeps its exact
|
|
202
|
+
// pre-β1 surface (a kept frame resolves again and throws there if truly
|
|
203
|
+
// broken; an offloaded one never resolves, as ever).
|
|
204
|
+
const windowIds = toolImageIdsInOrder.slice(-TOOL_IMAGE_KEEP_K);
|
|
205
|
+
const urlById = new Map();
|
|
206
|
+
const hashById = new Map();
|
|
207
|
+
for (const id of windowIds) {
|
|
208
|
+
const r = toolImageRefById.get(id);
|
|
209
|
+
if (r === undefined)
|
|
210
|
+
continue;
|
|
211
|
+
try {
|
|
212
|
+
const url = await imageCtx.resolver.resolveDataUrl(r);
|
|
213
|
+
urlById.set(id, url);
|
|
214
|
+
hashById.set(id, createHash("sha256").update(url).digest("hex"));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// No hash for this frame — the sentinel below keeps it unique, so it
|
|
218
|
+
// rides (or offloads) exactly as it would have without β1.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const verdicts = sameImageKeepVerdicts(windowIds.map((id) => hashById.get(id) ?? `unresolved:${id}`), TOOL_IMAGE_DEDUP_OMIT_CAP);
|
|
222
|
+
const dedupOmitIds = new Set();
|
|
223
|
+
windowIds.forEach((id, i) => {
|
|
224
|
+
if (verdicts[i] === false)
|
|
225
|
+
dedupOmitIds.add(id);
|
|
226
|
+
});
|
|
227
|
+
const isDedupStubbed = (b) => toolImageIds.has(b.ref.attachmentId) && dedupOmitIds.has(b.ref.attachmentId);
|
|
228
|
+
// Dedup survivors only: a note consumes no image budget, so a static loop
|
|
229
|
+
// also stops pressuring the M79 offload ladder (the bytes it would have
|
|
230
|
+
// spent are simply never resolved).
|
|
231
|
+
const blocks = collectRequestImages(input)
|
|
232
|
+
.filter((b) => !isWindowStubbed(b))
|
|
233
|
+
.filter((b) => !isDedupStubbed(b));
|
|
234
|
+
const { kept } = offloadRequestImages(blocks, imageCtx.budgetBytes);
|
|
235
|
+
const keptIds = new Set(kept.map((b) => b.ref.attachmentId));
|
|
236
|
+
for (const b of kept) {
|
|
237
|
+
if (!urlById.has(b.ref.attachmentId)) {
|
|
238
|
+
urlById.set(b.ref.attachmentId, await imageCtx.resolver.resolveDataUrl(b.ref));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const partFor = (b, fromTool) => {
|
|
242
|
+
// ω99-138: an evicted tool image renders as the fixed stub — same
|
|
243
|
+
// position, deterministic bytes (user images never take this branch).
|
|
244
|
+
if (fromTool && isWindowStubbed(b))
|
|
245
|
+
return { type: "text", text: TOOL_IMAGE_KEEP_K_STUB };
|
|
246
|
+
// β1: an adjacent byte-duplicate renders as the fixed dedup note — same
|
|
247
|
+
// position-stable discipline (user images never take this branch either).
|
|
248
|
+
if (fromTool && isDedupStubbed(b))
|
|
249
|
+
return { type: "text", text: TOOL_IMAGE_DEDUP_STUB };
|
|
250
|
+
return keptIds.has(b.ref.attachmentId)
|
|
251
|
+
? { type: "image_url", image_url: { url: urlById.get(b.ref.attachmentId) } }
|
|
252
|
+
: { type: "text", text: imageOffloadPlaceholder(b.ref) };
|
|
253
|
+
};
|
|
254
|
+
const replay = config.reasoningReplay ?? "never";
|
|
255
|
+
const out = [{ role: "system", content: input.systemPrompt }];
|
|
256
|
+
let toolImages = [];
|
|
257
|
+
const flushToolImages = () => {
|
|
258
|
+
if (toolImages.length === 0)
|
|
259
|
+
return;
|
|
260
|
+
out.push({
|
|
261
|
+
role: "user",
|
|
262
|
+
// ω99-138: each part follows its block's window verdict (image_url for
|
|
263
|
+
// the kept K, the fixed stub for the evicted ones — positions stable).
|
|
264
|
+
content: [{ type: "text", text: TOOL_IMAGE_NOTE }, ...toolImages.map((b) => partFor(b, true))],
|
|
265
|
+
});
|
|
266
|
+
toolImages = [];
|
|
267
|
+
};
|
|
268
|
+
for (const m of input.messages) {
|
|
269
|
+
const imgs = m.images;
|
|
270
|
+
const carries = Array.isArray(imgs) && imgs.length > 0;
|
|
271
|
+
if (m.role === "tool") {
|
|
272
|
+
out.push({
|
|
273
|
+
role: "tool",
|
|
274
|
+
// ω-1a: same outbound restore as toOpenAIMessage's tool case.
|
|
275
|
+
tool_call_id: restoreToolCallId(m.toolCallId, m.name),
|
|
276
|
+
content: carries ? (m.content !== "" ? m.content : TOOL_IMAGE_CONTENT_FALLBACK) : m.content,
|
|
277
|
+
});
|
|
278
|
+
if (carries)
|
|
279
|
+
toolImages.push(...imgs);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
// The synthetic carrier follows the tool block it answers.
|
|
283
|
+
flushToolImages();
|
|
284
|
+
if (m.role === "user" && carries) {
|
|
285
|
+
// r5-C1: an image-ONLY turn (empty caption) omits the empty text part —
|
|
286
|
+
// a bare {type:"text",text:""} is rejected by several strict
|
|
287
|
+
// OpenAI-compatible endpoints; images alone are a well-formed content
|
|
288
|
+
// array.
|
|
289
|
+
const parts = m.content === "" ? [] : [{ type: "text", text: m.content }];
|
|
290
|
+
// ω99-138: user images are OUTSIDE the keep-K window — fromTool=false
|
|
291
|
+
// is the byte-for-byte M79 path (zero drift).
|
|
292
|
+
out.push({ role: "user", content: [...parts, ...imgs.map((b) => partFor(b, false))] });
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
out.push(toOpenAIMessage(m, replay));
|
|
296
|
+
}
|
|
297
|
+
// Trailing tool results (a run cut mid-tool-phase) still carry their images.
|
|
298
|
+
flushToolImages();
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=openai-images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-images.js","sourceRoot":"","sources":["../src/openai-images.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ModelInput } from "@wudao-org/core";
|
|
2
|
+
import type { ReasoningReplay } from "./config.js";
|
|
3
|
+
export declare const MAX_ACCUM_CHARS = 2000000;
|
|
4
|
+
export declare const TRUNCATED_MARK = "[truncated at 2M chars]";
|
|
5
|
+
/** Cap a finished accumulator: truncate at the cap and append the marker. */
|
|
6
|
+
export declare function capAccumulated(s: string): string;
|
|
7
|
+
/** ω-2a: is this reasoning string a tail the CLIENT truncated at the
|
|
8
|
+
* accumulator cap? The in-band marker is the truncation signal (every cap
|
|
9
|
+
* site appends exactly it) — a reasoning string ending in it is incomplete
|
|
10
|
+
* by construction and must never be treated as a complete tail. */
|
|
11
|
+
export declare function isCappedReasoning(reasoning: string | undefined): boolean;
|
|
12
|
+
export declare function toOpenAIMessage(m: ModelInput["messages"][number], replay?: ReasoningReplay): {
|
|
13
|
+
reasoning_content?: string | undefined;
|
|
14
|
+
tool_calls?: {
|
|
15
|
+
id: string;
|
|
16
|
+
type: string;
|
|
17
|
+
function: {
|
|
18
|
+
name: string;
|
|
19
|
+
arguments: string;
|
|
20
|
+
};
|
|
21
|
+
}[] | undefined;
|
|
22
|
+
role: string;
|
|
23
|
+
content: string;
|
|
24
|
+
tool_call_id?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
role: string;
|
|
27
|
+
tool_call_id: string;
|
|
28
|
+
content: string;
|
|
29
|
+
};
|
|
30
|
+
/** JSON with recursively sorted object keys (arrays keep their order). */
|
|
31
|
+
export declare function canonicalJson(value: unknown): string;
|
|
32
|
+
/** Deterministic byte serialization of a ModelInput's prompt content. NOTE
|
|
33
|
+
* (M47 §1): this pins the message SET (system+tools+messages) — the
|
|
34
|
+
* reasoning_replay field is provider-config-side and rides the wire only when
|
|
35
|
+
* `reasoningReplay` is enabled; its wire-shape pins live in the replay tests
|
|
36
|
+
* (raw request bodies), not here. M79: image-bearing turns pin their
|
|
37
|
+
* ImageAttachmentRefs (durable form); a text-only session's bytes are
|
|
38
|
+
* IDENTICAL to pre-M79. */
|
|
39
|
+
export declare function serializePrompt(input: ModelInput): string;
|
|
40
|
+
//# sourceMappingURL=openai-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-messages.d.ts","sourceRoot":"","sources":["../src/openai-messages.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAyB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AASnD,eAAO,MAAM,eAAe,UAAY,CAAC;AACzC,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;oEAGoE;AACpE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAExE;AAgBD,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,GAAE,eAAyB;;;;;;;;;;;;;;;;;EAwBnG;AAkBD,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASpD;AA8BD;;;;;;4BAM4B;AAC5B,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWzD"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { truncateSafe } from "@wudao-org/core";
|
|
2
|
+
import { restoreToolCallId, restoreToolCallIds } from "./tool-call-ids.js";
|
|
3
|
+
// OOM fix: per-response accumulation caps. A degenerate endpoint (or an
|
|
4
|
+
// adversarial stream) can pump unbounded reasoning/content/argument bytes
|
|
5
|
+
// into a single response; every accumulator is capped so no downstream
|
|
6
|
+
// consumer (driver live state, history, persistence) can ever hold an
|
|
7
|
+
// unbounded per-response string. 2M chars ≈ the largest legitimate response
|
|
8
|
+
// by an order of magnitude; truncation is always marked.
|
|
9
|
+
export const MAX_ACCUM_CHARS = 2_000_000;
|
|
10
|
+
export const TRUNCATED_MARK = "[truncated at 2M chars]";
|
|
11
|
+
/** Cap a finished accumulator: truncate at the cap and append the marker. */
|
|
12
|
+
export function capAccumulated(s) {
|
|
13
|
+
return s.length > MAX_ACCUM_CHARS ? truncateSafe(s, MAX_ACCUM_CHARS) + TRUNCATED_MARK : s; // 113-A4
|
|
14
|
+
}
|
|
15
|
+
/** ω-2a: is this reasoning string a tail the CLIENT truncated at the
|
|
16
|
+
* accumulator cap? The in-band marker is the truncation signal (every cap
|
|
17
|
+
* site appends exactly it) — a reasoning string ending in it is incomplete
|
|
18
|
+
* by construction and must never be treated as a complete tail. */
|
|
19
|
+
export function isCappedReasoning(reasoning) {
|
|
20
|
+
return reasoning !== undefined && reasoning.endsWith(TRUNCATED_MARK);
|
|
21
|
+
}
|
|
22
|
+
/** M47 §1: does this assistant message's stored reasoning ride back out?
|
|
23
|
+
* Byte discipline: the value is passed through VERBATIM — never trimmed,
|
|
24
|
+
* never reformatted — so the replayed bytes equal the stored bytes. */
|
|
25
|
+
function replayReasoning(m, replay) {
|
|
26
|
+
if (replay === "never" || m.reasoning === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
// ω-2a: a client-capped tail is incomplete — never replayed as complete.
|
|
29
|
+
// Omitting the optional reasoning_content keeps the request wire-valid
|
|
30
|
+
// (the pre-M47 shape every "never"-mode session already sends).
|
|
31
|
+
if (isCappedReasoning(m.reasoning))
|
|
32
|
+
return undefined;
|
|
33
|
+
if (replay === "always")
|
|
34
|
+
return m.reasoning;
|
|
35
|
+
// toolCallTurns: only the turns that carry tool calls (CLAUDE.md §4.4).
|
|
36
|
+
return m.toolCalls !== undefined && m.toolCalls.length > 0 ? m.reasoning : undefined;
|
|
37
|
+
}
|
|
38
|
+
export function toOpenAIMessage(m, replay = "never") {
|
|
39
|
+
switch (m.role) {
|
|
40
|
+
case "user":
|
|
41
|
+
return { role: "user", content: m.content };
|
|
42
|
+
case "assistant": {
|
|
43
|
+
const reasoning = replayReasoning(m, replay);
|
|
44
|
+
return {
|
|
45
|
+
role: "assistant",
|
|
46
|
+
content: m.content,
|
|
47
|
+
// ω-1a: the stored ids may carry the dedupe's `#n` suffix — the wire
|
|
48
|
+
// gets the provider-native shape back (real ids strip to themselves).
|
|
49
|
+
...(m.toolCalls && m.toolCalls.length > 0
|
|
50
|
+
? { tool_calls: restoreToolCallIds(m.toolCalls).map((tc) => ({ id: tc.id, type: "function", function: { name: tc.name, arguments: JSON.stringify(tc.arguments) } })) }
|
|
51
|
+
: {}),
|
|
52
|
+
// Appended AFTER the pinned fields: the default (never) request bytes
|
|
53
|
+
// are unchanged, and an enabled replay only grows the message tail.
|
|
54
|
+
...(reasoning !== undefined ? { reasoning_content: reasoning } : {}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
case "tool":
|
|
58
|
+
// ω-1a: tool_result ids restore in lockstep with the assistant calls
|
|
59
|
+
// they answer (a stripped call with a suffixed result would 400).
|
|
60
|
+
return { role: "tool", tool_call_id: restoreToolCallId(m.toolCallId, m.name), content: m.content };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Canonical prompt serialization (M24).
|
|
65
|
+
//
|
|
66
|
+
// serializePrompt is THE one serializer every byte-prefix assertion uses
|
|
67
|
+
// (det-06, fork prefix sharing, CLI miss attribution) — nowhere else may a
|
|
68
|
+
// hand-rolled comparison live. Shape:
|
|
69
|
+
// - one JSON line per segment, each terminated by "\n";
|
|
70
|
+
// - stable header FIRST (system line, then one line per tool), messages
|
|
71
|
+
// after — mirroring where the prompt bytes actually diverge: appending a
|
|
72
|
+
// message (append-only growth) only ever APPENDS bytes at the tail, so
|
|
73
|
+
// request N's serialization is a byte-prefix of request N+1's whenever
|
|
74
|
+
// the history grew append-only and the header stayed frozen;
|
|
75
|
+
// - canonical key order (recursive sort) — the caller's insertion order
|
|
76
|
+
// never leaks into the bytes.
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
/** JSON with recursively sorted object keys (arrays keep their order). */
|
|
79
|
+
export function canonicalJson(value) {
|
|
80
|
+
if (Array.isArray(value))
|
|
81
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
82
|
+
if (value !== null && typeof value === "object") {
|
|
83
|
+
const entries = Object.entries(value)
|
|
84
|
+
.filter(([, v]) => v !== undefined)
|
|
85
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
86
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
|
|
87
|
+
}
|
|
88
|
+
return JSON.stringify(value);
|
|
89
|
+
}
|
|
90
|
+
/** M79: the CANONICAL (durable) form of one message. Image-bearing turns
|
|
91
|
+
* serialize their REFS — the transient data URL never appears here (it
|
|
92
|
+
* exists only inside one request build, and never in a byte-pin). Text-only
|
|
93
|
+
* turns return exactly toOpenAIMessage's shape, so every pre-M79 golden
|
|
94
|
+
* byte is unchanged. ω99-55: FILE-bearing user turns pin their blocks
|
|
95
|
+
* (same durable discipline — without this, a file turn and a text turn
|
|
96
|
+
* with equal content would pin identical bytes and the pin would lose the
|
|
97
|
+
* file identity; the projected handle is derived text, versioned by the
|
|
98
|
+
* projection, and rides only the wire). */
|
|
99
|
+
function canonicalMessage(m) {
|
|
100
|
+
const imgs = m.images;
|
|
101
|
+
const files = m.role === "user" ? m.files : undefined;
|
|
102
|
+
const hasImgs = Array.isArray(imgs) && imgs.length > 0;
|
|
103
|
+
const hasFiles = Array.isArray(files) && files.length > 0;
|
|
104
|
+
if (hasImgs || hasFiles) {
|
|
105
|
+
if (m.role === "user") {
|
|
106
|
+
return {
|
|
107
|
+
role: "user",
|
|
108
|
+
content: m.content,
|
|
109
|
+
...(hasImgs ? { images: imgs } : {}),
|
|
110
|
+
...(hasFiles ? { files } : {}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (m.role === "tool")
|
|
114
|
+
return { role: "tool", tool_call_id: m.toolCallId, content: m.content, images: imgs };
|
|
115
|
+
}
|
|
116
|
+
return toOpenAIMessage(m);
|
|
117
|
+
}
|
|
118
|
+
/** Deterministic byte serialization of a ModelInput's prompt content. NOTE
|
|
119
|
+
* (M47 §1): this pins the message SET (system+tools+messages) — the
|
|
120
|
+
* reasoning_replay field is provider-config-side and rides the wire only when
|
|
121
|
+
* `reasoningReplay` is enabled; its wire-shape pins live in the replay tests
|
|
122
|
+
* (raw request bodies), not here. M79: image-bearing turns pin their
|
|
123
|
+
* ImageAttachmentRefs (durable form); a text-only session's bytes are
|
|
124
|
+
* IDENTICAL to pre-M79. */
|
|
125
|
+
export function serializePrompt(input) {
|
|
126
|
+
const lines = [canonicalJson({ role: "system", content: input.systemPrompt })];
|
|
127
|
+
for (const t of input.tools) {
|
|
128
|
+
lines.push(canonicalJson({ type: "function", function: { name: t.name, description: t.description, parameters: t.parameters } }));
|
|
129
|
+
}
|
|
130
|
+
for (const m of input.messages) {
|
|
131
|
+
lines.push(canonicalJson(canonicalMessage(m)));
|
|
132
|
+
}
|
|
133
|
+
return lines.map((l) => `${l}\n`).join("");
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=openai-messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-messages.js","sourceRoot":"","sources":["../src/openai-messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ModelInput, ModelUsage } from "@wudao-org/core";
|
|
2
|
+
import type { ProviderConfig, ReasoningEffort, ReasoningReplay } from "./config.js";
|
|
3
|
+
import { type ImageRequestContext } from "./openai-images.js";
|
|
4
|
+
export interface MandatoryReasoningFlag {
|
|
5
|
+
applied: boolean;
|
|
6
|
+
onApplied?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export interface MandatoryReasoningCall {
|
|
9
|
+
readonly flag: MandatoryReasoningFlag;
|
|
10
|
+
/** The repair body once armed (per call); undefined until a repairable
|
|
11
|
+
* rejection classified or the client came pre-armed. */
|
|
12
|
+
body?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
/** Create the per-call runtime; a pre-armed client starts with the repair
|
|
15
|
+
* body already set (the leg's own config shapes it). */
|
|
16
|
+
export declare function createMandatoryReasoningCall(flag: MandatoryReasoningFlag, config: ProviderConfig): MandatoryReasoningCall;
|
|
17
|
+
/** Arm the client flag (idempotent) after a repaired request was served. */
|
|
18
|
+
export declare function armMandatoryReasoningFlag(flag: MandatoryReasoningFlag): void;
|
|
19
|
+
/** The request's EFFECTIVE reasoning effort — a per-input effort (already
|
|
20
|
+
* parsed once at the public boundary) overrides the config's own. The single
|
|
21
|
+
* source both the request body and the ω-2a repair gates read, so a gate can
|
|
22
|
+
* never disagree with the bytes that actually went out. */
|
|
23
|
+
export declare function effectiveReasoningEffort(input: Pick<ModelInput, "reasoningEffort">, config: ProviderConfig): ReasoningEffort | undefined;
|
|
24
|
+
export interface ChatCompletionResponse {
|
|
25
|
+
choices?: Array<{
|
|
26
|
+
finish_reason?: string;
|
|
27
|
+
message: {
|
|
28
|
+
role: string;
|
|
29
|
+
content?: string;
|
|
30
|
+
reasoning_content?: string;
|
|
31
|
+
reasoning?: string;
|
|
32
|
+
thinking?: string;
|
|
33
|
+
tool_calls?: Array<{
|
|
34
|
+
id: string;
|
|
35
|
+
type: string;
|
|
36
|
+
function: {
|
|
37
|
+
name: string;
|
|
38
|
+
arguments: string;
|
|
39
|
+
};
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
}>;
|
|
43
|
+
usage?: WireUsage;
|
|
44
|
+
}
|
|
45
|
+
/** Wire usage shapes across OpenAI-compatible servers. */
|
|
46
|
+
export interface WireUsage {
|
|
47
|
+
prompt_tokens?: number;
|
|
48
|
+
completion_tokens?: number;
|
|
49
|
+
/** DeepSeek spelling. `prompt_tokens` INCLUDES these hits. */
|
|
50
|
+
prompt_cache_hit_tokens?: number;
|
|
51
|
+
/** DeepSeek spelling of the miss count (the tokens this call wrote to / paid
|
|
52
|
+
* uncached input for). */
|
|
53
|
+
prompt_cache_miss_tokens?: number;
|
|
54
|
+
/** Explicit cache-write spelling (endpoints that report it separately). */
|
|
55
|
+
prompt_cache_write_tokens?: number;
|
|
56
|
+
/** OpenAI spelling of the same quantities. */
|
|
57
|
+
prompt_tokens_details?: {
|
|
58
|
+
cached_tokens?: number;
|
|
59
|
+
};
|
|
60
|
+
completion_tokens_details?: {
|
|
61
|
+
reasoning_tokens?: number;
|
|
62
|
+
};
|
|
63
|
+
/** Flat reasoning spelling some gateways use instead of the details object. */
|
|
64
|
+
reasoning_tokens?: number;
|
|
65
|
+
}
|
|
66
|
+
/** Disjoint-count mapping (dsh rule): `prompt_tokens` includes cache hits, and
|
|
67
|
+
* the two wire spellings report the SAME quantity — prefer details, fall back
|
|
68
|
+
* to the flat field, never sum them (no double counting). Miss = the rest
|
|
69
|
+
* (DeepSeek's explicit miss spelling wins when present).
|
|
70
|
+
*
|
|
71
|
+
* M56 §2 (turn_usage, omit-never-null): `cacheWriteTokens` is set only when
|
|
72
|
+
* the wire actually reported a cache split — on DeepSeek-style endpoints
|
|
73
|
+
* every miss is written, so cache-write ≈ miss; `reasoningTokens` rides the
|
|
74
|
+
* completion details spelling when the endpoint reports it. Everything
|
|
75
|
+
* unknown stays OMITTED (never null, never zero-backfilled). */
|
|
76
|
+
export declare function toModelUsage(u: WireUsage): ModelUsage;
|
|
77
|
+
/** M56 §2: the receipt's client-side extras — the wall duration measured
|
|
78
|
+
* around the attempt (always set; CodeWhale's duration_ms-always rule) and
|
|
79
|
+
* the replayed-reasoning estimate (only when the replay contract sent bytes
|
|
80
|
+
* back out). */
|
|
81
|
+
export declare function enrichUsage(usage: ModelUsage, durationMs: number, replayTokens: number): ModelUsage;
|
|
82
|
+
/** Client-side estimate of the reasoning tokens sent BACK to the model this
|
|
83
|
+
* call (the M47 replay contract): CJK chars ≈ 1 token, ~4 others per token.
|
|
84
|
+
* An estimate by construction — no endpoint reports it. */
|
|
85
|
+
export declare function replayedReasoningTokens(messages: readonly ModelInput["messages"][number][], replay: ReasoningReplay): number;
|
|
86
|
+
/** The one request body builder (M79): the pre-M79 byte-identical
|
|
87
|
+
* construction when no images ride the input, the image walk above when
|
|
88
|
+
* they do. ω99-42: threads the mandatory-reasoning repair body through.
|
|
89
|
+
* ω99-55: FILE blocks project to handle text FIRST (dsh LlmRuntime parity
|
|
90
|
+
* — no wire ever receives file bytes); the projection is the identity when
|
|
91
|
+
* nothing rides files, so text/image request bytes are untouched. */
|
|
92
|
+
export declare function buildOpenAIRequestBody(config: ProviderConfig, input: ModelInput, imageCtx: ImageRequestContext, mr: MandatoryReasoningCall): Promise<Record<string, unknown>>;
|
|
93
|
+
//# sourceMappingURL=openai-wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-wire.d.ts","sourceRoot":"","sources":["../src/openai-wire.ts"],"names":[],"mappings":"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"}
|