dsh-lcx-codex 0.3.3 → 0.4.0-rc.13
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/ARCHITECTURE.md +93 -0
- package/CHANGELOG.md +143 -41
- package/README.md +208 -100
- package/README_EN.md +133 -101
- package/assets/dsh-lcx-codex-banner.jpg +0 -0
- package/cordis.patch.yml +10 -4
- package/lib/client.js +117 -194
- package/lib/compact-v2.js +124 -273
- package/lib/dsh-responses.js +299 -0
- package/lib/index.js +548 -1593
- package/lib/json-store.js +34 -0
- package/lib/legacy-v3.js +20 -0
- package/lib/native-checkpoint.js +93 -0
- package/lib/responses-replay.js +281 -0
- package/lib/route.js +222 -0
- package/lib/service-mutex.js +72 -0
- package/lib/transport.js +59 -380
- package/lib/web-run-output.js +13 -152
- package/lib/web-search-alpha.js +26 -448
- package/lib/web-search-capability.js +8 -10
- package/lib/web-search-hosted.js +22 -372
- package/lib/web-search-ref-store.js +7 -7
- package/package.json +20 -14
- package/scripts/probe-alpha.mjs +28 -76
- package/scripts/validate-dsh-schema.mjs +31 -0
- package/lib/checkpoint-store-v3.js +0 -459
- package/lib/compact.js +0 -752
- package/lib/dsh-pi-responses.js +0 -339
- package/lib/private-file.js +0 -18
- package/lib/session-lease.js +0 -70
- package/lib/web-search-store.js +0 -181
package/lib/compact-v2.js
CHANGED
|
@@ -1,307 +1,158 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { consumeSse, fetchSseWithRetry } from './transport.js'
|
|
2
|
+
import { responsesTools } from './dsh-responses.js'
|
|
3
3
|
|
|
4
|
-
const REMOTE_COMPACTION_V2_FEATURE = 'remote_compaction_v2'
|
|
5
|
-
const DEFAULT_MAX_SSE_BYTES = 8 * 1024 * 1024
|
|
4
|
+
export const REMOTE_COMPACTION_V2_FEATURE = 'remote_compaction_v2'
|
|
6
5
|
|
|
7
|
-
function isObject(value) {
|
|
8
|
-
|
|
6
|
+
function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value) }
|
|
7
|
+
function fail(message, code = 'LCX_COMPACT_INVALID_RESPONSE') { const e = new Error(message); e.code = code; return e }
|
|
8
|
+
function safeMachineField(value) {
|
|
9
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > 96) return undefined
|
|
10
|
+
return /^[A-Za-z0-9_.:\[\]-]+$/u.test(value) ? value : undefined
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
function
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const outputIndex = Number.isInteger(event?.output_index) && event.output_index >= 0
|
|
19
|
-
? `index:${event.output_index}`
|
|
20
|
-
: undefined
|
|
21
|
-
const itemIds = [event?.item?.id, event?.item_id]
|
|
22
|
-
.filter((value) => typeof value === 'string' && value.length > 0)
|
|
23
|
-
.map((value) => `id:${value}`)
|
|
24
|
-
const uniqueItemIds = [...new Set(itemIds)]
|
|
25
|
-
if (uniqueItemIds.length > 1) {
|
|
26
|
-
throw protocolError('Native compaction SSE event contains conflicting item identities')
|
|
27
|
-
}
|
|
28
|
-
return { outputIndex, itemId: uniqueItemIds[0] }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function mergeOutputItem(event, items, recordsByIndex, recordsById) {
|
|
32
|
-
if (!isObject(event?.item)) return
|
|
33
|
-
const { outputIndex, itemId } = outputItemIdentity(event)
|
|
34
|
-
const mappedRecords = new Set()
|
|
35
|
-
if (outputIndex && recordsByIndex.has(outputIndex)) mappedRecords.add(recordsByIndex.get(outputIndex))
|
|
36
|
-
if (itemId && recordsById.has(itemId)) mappedRecords.add(recordsById.get(itemId))
|
|
37
|
-
if (mappedRecords.size > 1) {
|
|
38
|
-
throw protocolError('Native compaction SSE event maps output_index and item id to different items')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let record = mappedRecords.values().next().value
|
|
42
|
-
if (!record && event.type === 'response.output_item.done') {
|
|
43
|
-
const pending = items.filter((candidate) => !candidate.done &&
|
|
44
|
-
(!candidate.item.type || !event.item.type || candidate.item.type === event.item.type))
|
|
45
|
-
if (pending.length === 1) record = pending[0]
|
|
46
|
-
else if (pending.length > 1) {
|
|
47
|
-
throw protocolError('Native compaction SSE output item has ambiguous aliases')
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (!record) {
|
|
51
|
-
record = { item: {}, outputIndex: undefined, itemId: undefined, done: false }
|
|
52
|
-
items.push(record)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (outputIndex && record.outputIndex && outputIndex !== record.outputIndex) {
|
|
56
|
-
throw protocolError('Native compaction SSE output_index changed for one item')
|
|
57
|
-
}
|
|
58
|
-
if (itemId && record.itemId && itemId !== record.itemId) {
|
|
59
|
-
throw protocolError('Native compaction SSE item id changed for one item')
|
|
60
|
-
}
|
|
61
|
-
if (outputIndex && recordsByIndex.has(outputIndex) && recordsByIndex.get(outputIndex) !== record) {
|
|
62
|
-
throw protocolError('Native compaction SSE output_index is reused by different items')
|
|
63
|
-
}
|
|
64
|
-
if (itemId && recordsById.has(itemId) && recordsById.get(itemId) !== record) {
|
|
65
|
-
throw protocolError('Native compaction SSE item id is reused by different items')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const previousEncryptedContent = typeof record.item.encrypted_content === 'string' && record.item.encrypted_content.length > 0
|
|
69
|
-
? record.item.encrypted_content
|
|
70
|
-
: undefined
|
|
71
|
-
Object.assign(record.item, structuredClone(event.item))
|
|
72
|
-
if ((!record.item.encrypted_content || typeof record.item.encrypted_content !== 'string') && previousEncryptedContent) {
|
|
73
|
-
record.item.encrypted_content = previousEncryptedContent
|
|
74
|
-
}
|
|
75
|
-
if (outputIndex) {
|
|
76
|
-
record.outputIndex = outputIndex
|
|
77
|
-
recordsByIndex.set(outputIndex, record)
|
|
78
|
-
}
|
|
79
|
-
if (itemId) {
|
|
80
|
-
record.itemId = itemId
|
|
81
|
-
recordsById.set(itemId, record)
|
|
82
|
-
}
|
|
83
|
-
if (event.type === 'response.output_item.done') record.done = true
|
|
13
|
+
export function mergeFeatureHeader(headers = {}) {
|
|
14
|
+
const result = { ...headers }
|
|
15
|
+
const key = Object.keys(result).find((name) => name.toLowerCase() === 'x-codex-beta-features')
|
|
16
|
+
const values = String(key ? result[key] ?? '' : '').split(',').map((x) => x.trim()).filter(Boolean)
|
|
17
|
+
if (!values.includes(REMOTE_COMPACTION_V2_FEATURE)) values.push(REMOTE_COMPACTION_V2_FEATURE)
|
|
18
|
+
result[key ?? 'x-codex-beta-features'] = values.join(',')
|
|
19
|
+
return result
|
|
84
20
|
}
|
|
85
21
|
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} catch {
|
|
92
|
-
return false
|
|
22
|
+
export function responsesGenerationEnvelope({ reasoningEffort, temperature, maxTokens } = {}) {
|
|
23
|
+
const result = {}
|
|
24
|
+
if (reasoningEffort !== undefined && reasoningEffort !== 'off') {
|
|
25
|
+
result.reasoning = { effort: String(reasoningEffort), summary: 'auto' }
|
|
26
|
+
result.include = ['reasoning.encrypted_content']
|
|
93
27
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const usage = response?.usage
|
|
98
|
-
if (!isObject(usage)) return undefined
|
|
99
|
-
const totalInputTokens = Number(usage.input_tokens ?? 0)
|
|
100
|
-
const outputTokens = Number(usage.output_tokens ?? 0)
|
|
101
|
-
const cacheReadValue = Number(usage.input_tokens_details?.cached_tokens ?? usage.input_token_details?.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? 0)
|
|
102
|
-
const cacheWriteValue = Number(usage.input_tokens_details?.cache_write_tokens ?? usage.input_token_details?.cache_write_tokens ?? usage.prompt_tokens_details?.cache_write_tokens ?? 0)
|
|
103
|
-
const cacheReadTokens = Number.isFinite(cacheReadValue) && cacheReadValue > 0 ? cacheReadValue : 0
|
|
104
|
-
const cacheWriteTokens = Number.isFinite(cacheWriteValue) && cacheWriteValue > 0 ? cacheWriteValue : 0
|
|
105
|
-
const inputTokens = Number.isFinite(totalInputTokens)
|
|
106
|
-
? Math.max(0, totalInputTokens - cacheReadTokens - cacheWriteTokens)
|
|
107
|
-
: 0
|
|
108
|
-
const normalizedOutputTokens = Number.isFinite(outputTokens) && outputTokens > 0 ? outputTokens : 0
|
|
109
|
-
if (![inputTokens, cacheReadTokens, cacheWriteTokens, normalizedOutputTokens].some((value) => value > 0)) return undefined
|
|
110
|
-
return {
|
|
111
|
-
inputTokens,
|
|
112
|
-
outputTokens: normalizedOutputTokens,
|
|
113
|
-
...(cacheReadTokens > 0 ? { cacheReadTokens } : {}),
|
|
114
|
-
...(cacheWriteTokens > 0 ? { cacheWriteTokens } : {}),
|
|
115
|
-
...(Number.isFinite(usage.output_token_details?.reasoning_tokens) && usage.output_token_details.reasoning_tokens > 0
|
|
116
|
-
? { reasoningTokens: Number(usage.output_token_details.reasoning_tokens) }
|
|
117
|
-
: {}),
|
|
28
|
+
if (temperature !== undefined) {
|
|
29
|
+
if (!Number.isFinite(temperature)) throw fail('Responses temperature must be finite', 'LCX_COMPACT_INVALID_INPUT')
|
|
30
|
+
result.temperature = Number(temperature)
|
|
118
31
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!isObject(tool)) return false
|
|
123
|
-
if (tool.type === undefined) return typeof tool.name === 'string' && tool.name.length > 0
|
|
124
|
-
if (typeof tool.type !== 'string' || tool.type.length === 0) return false
|
|
125
|
-
if (tool.type === 'function' || tool.type === 'custom') {
|
|
126
|
-
const name = tool.name ?? tool.function?.name
|
|
127
|
-
return typeof name === 'string' && name.length > 0
|
|
32
|
+
if (maxTokens !== undefined) {
|
|
33
|
+
if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw fail('Responses maxTokens must be a positive safe integer', 'LCX_COMPACT_INVALID_INPUT')
|
|
34
|
+
result.max_output_tokens = Math.max(16, maxTokens)
|
|
128
35
|
}
|
|
129
|
-
return true
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function responsesTools(tools) {
|
|
133
|
-
if (tools === undefined) return undefined
|
|
134
|
-
if (!Array.isArray(tools) || !tools.every(validToolDeclaration)) {
|
|
135
|
-
throw protocolError('Native compaction tools contain an invalid declaration', 'LCX_COMPACT_INVALID_TOOLS')
|
|
136
|
-
}
|
|
137
|
-
return tools.map((tool) => {
|
|
138
|
-
if (tool.type !== undefined) return structuredClone(tool)
|
|
139
|
-
return {
|
|
140
|
-
type: 'function',
|
|
141
|
-
...structuredClone(tool),
|
|
142
|
-
description: typeof tool.description === 'string' ? tool.description : '',
|
|
143
|
-
parameters: tool.parameters && typeof tool.parameters === 'object' ? structuredClone(tool.parameters) : { type: 'object', properties: {} },
|
|
144
|
-
strict: false,
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function mergeFeatureHeader(headers) {
|
|
150
|
-
const result = { ...(headers ?? {}) }
|
|
151
|
-
const key = Object.keys(result).find((name) => name.toLowerCase() === 'x-codex-beta-features')
|
|
152
|
-
const existing = key ? String(result[key] ?? '').trim() : ''
|
|
153
|
-
const features = existing.split(',').map((value) => value.trim()).filter(Boolean)
|
|
154
|
-
if (!features.includes(REMOTE_COMPACTION_V2_FEATURE)) features.push(REMOTE_COMPACTION_V2_FEATURE)
|
|
155
|
-
result[key ?? 'x-codex-beta-features'] = features.join(',')
|
|
156
36
|
return result
|
|
157
37
|
}
|
|
158
38
|
|
|
159
|
-
export function buildNativeCompactionBody({ model, input, instructions, promptCacheKey, promptCacheRetention,
|
|
160
|
-
if (!Array.isArray(input)) throw
|
|
161
|
-
if (input.some((item) =>
|
|
162
|
-
throw protocolError('Native compaction input already contains compaction_trigger', 'LCX_COMPACT_DUPLICATE_TRIGGER')
|
|
163
|
-
}
|
|
39
|
+
export function buildNativeCompactionBody({ model, input, instructions, tools, promptCacheKey, promptCacheRetention, reasoningEffort, temperature, maxTokens }) {
|
|
40
|
+
if (!Array.isArray(input)) throw fail('native compaction input must be an array', 'LCX_COMPACT_INVALID_INPUT')
|
|
41
|
+
if (input.some((item) => item?.type === 'compaction_trigger')) throw fail('native compaction input already contains compaction_trigger', 'LCX_COMPACT_DUPLICATE_TRIGGER')
|
|
164
42
|
const nativeTools = responsesTools(tools)
|
|
165
43
|
return {
|
|
166
44
|
model,
|
|
167
45
|
input: [...structuredClone(input), { type: 'compaction_trigger' }],
|
|
168
46
|
stream: true,
|
|
169
47
|
store: false,
|
|
48
|
+
tool_choice: 'auto',
|
|
49
|
+
parallel_tool_calls: true,
|
|
50
|
+
...(instructions !== undefined ? { instructions } : {}),
|
|
170
51
|
...(nativeTools !== undefined ? { tools: nativeTools } : {}),
|
|
171
|
-
...(
|
|
172
|
-
...(
|
|
173
|
-
...(
|
|
52
|
+
...(promptCacheKey ? { prompt_cache_key: String(promptCacheKey) } : {}),
|
|
53
|
+
...(promptCacheRetention ? { prompt_cache_retention: String(promptCacheRetention) } : {}),
|
|
54
|
+
...responsesGenerationEnvelope({ reasoningEffort, temperature, maxTokens }),
|
|
174
55
|
}
|
|
175
56
|
}
|
|
176
57
|
|
|
177
|
-
function
|
|
178
|
-
if (
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
58
|
+
function usageFrom(raw) {
|
|
59
|
+
if (!isObject(raw)) return undefined
|
|
60
|
+
const inputTotal = Number(raw.input_tokens ?? 0)
|
|
61
|
+
const output = Number(raw.output_tokens ?? 0)
|
|
62
|
+
const details = raw.input_tokens_details ?? raw.input_token_details ?? raw.prompt_tokens_details ?? {}
|
|
63
|
+
const cacheRead = Number(details.cached_tokens ?? 0)
|
|
64
|
+
const cacheWrite = Number(details.cache_write_tokens ?? 0)
|
|
65
|
+
const result = {
|
|
66
|
+
inputTokens: Math.max(0, Number.isFinite(inputTotal) ? inputTotal - (Number.isFinite(cacheRead) ? cacheRead : 0) - (Number.isFinite(cacheWrite) ? cacheWrite : 0) : 0),
|
|
67
|
+
outputTokens: Number.isFinite(output) && output > 0 ? output : 0,
|
|
68
|
+
}
|
|
69
|
+
if (Number.isFinite(cacheRead) && cacheRead > 0) result.cacheReadTokens = cacheRead
|
|
70
|
+
if (Number.isFinite(cacheWrite) && cacheWrite > 0) result.cacheWriteTokens = cacheWrite
|
|
71
|
+
const reasoning = Number(raw.output_tokens_details?.reasoning_tokens ?? raw.output_token_details?.reasoning_tokens ?? 0)
|
|
72
|
+
if (Number.isFinite(reasoning) && reasoning > 0) result.reasoningTokens = reasoning
|
|
73
|
+
return Object.values(result).some((v) => Number(v) > 0) ? result : undefined
|
|
188
74
|
}
|
|
189
75
|
|
|
190
|
-
|
|
191
|
-
if (!
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const event = parseSseEvent(dataLines)
|
|
207
|
-
dataLines = []
|
|
208
|
-
if (!event) return
|
|
209
|
-
if (event.type === 'error' || event.type === 'response.failed' || event.type === 'response.incomplete') {
|
|
210
|
-
const message = event.error?.message ?? event.response?.error?.message ?? `Native compaction ended with ${String(event.type)}`
|
|
211
|
-
throw protocolError(message, 'LCX_COMPACT_UPSTREAM_ERROR')
|
|
212
|
-
}
|
|
213
|
-
if (event.type === 'response.output_item.added' || event.type === 'response.output_item.done') {
|
|
214
|
-
mergeOutputItem(event, eventItems, recordsByIndex, recordsById)
|
|
215
|
-
}
|
|
216
|
-
if (event.type === 'response.completed') {
|
|
217
|
-
sawTerminal = true
|
|
218
|
-
completedResponse = isObject(event.response) ? event.response : {}
|
|
219
|
-
completedOutput = Array.isArray(completedResponse.output) ? structuredClone(completedResponse.output) : undefined
|
|
76
|
+
function validateOutput(output) {
|
|
77
|
+
if (!Array.isArray(output) || output.length === 0) throw fail('native compaction output is empty')
|
|
78
|
+
const compactions = output.filter((item) => isObject(item) && item.type === 'compaction')
|
|
79
|
+
if (compactions.length !== 1) throw fail(`native compaction must return exactly one compaction item, got ${compactions.length}`)
|
|
80
|
+
const compact = compactions[0]
|
|
81
|
+
if (typeof compact.encrypted_content !== 'string' || compact.encrypted_content.length === 0) throw fail('native compaction item has no encrypted_content')
|
|
82
|
+
const calls = new Set()
|
|
83
|
+
const results = new Set()
|
|
84
|
+
for (const item of output) {
|
|
85
|
+
if (!isObject(item) || typeof item.type !== 'string') throw fail('native compaction output contains a malformed item')
|
|
86
|
+
if (item.type === 'function_call') {
|
|
87
|
+
if (typeof item.call_id !== 'string' || !item.call_id || calls.has(item.call_id)) throw fail('native compaction output contains an invalid function_call')
|
|
88
|
+
calls.add(item.call_id)
|
|
89
|
+
} else if (item.type === 'function_call_output') {
|
|
90
|
+
if (typeof item.call_id !== 'string' || !item.call_id || results.has(item.call_id)) throw fail('native compaction output contains an invalid function_call_output')
|
|
91
|
+
results.add(item.call_id)
|
|
220
92
|
}
|
|
221
93
|
}
|
|
94
|
+
for (const id of calls) if (!results.has(id)) throw fail(`native compaction output has orphan function_call ${id}`)
|
|
95
|
+
for (const id of results) if (!calls.has(id)) throw fail(`native compaction output has orphan function_call_output ${id}`)
|
|
96
|
+
return { output: structuredClone(output), compaction: structuredClone(compact) }
|
|
97
|
+
}
|
|
222
98
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
99
|
+
export async function parseNativeCompactionSse(response, options = {}) {
|
|
100
|
+
const byIndex = new Map()
|
|
101
|
+
const byId = new Map()
|
|
102
|
+
let terminal
|
|
103
|
+
const mergeItem = (event) => {
|
|
104
|
+
if (!isObject(event?.item)) return
|
|
105
|
+
const index = Number.isInteger(event.output_index) ? event.output_index : undefined
|
|
106
|
+
const id = typeof event.item.id === 'string' ? event.item.id : typeof event.item_id === 'string' ? event.item_id : undefined
|
|
107
|
+
let record = (index !== undefined ? byIndex.get(index) : undefined) ?? (id ? byId.get(id) : undefined)
|
|
108
|
+
if (!record) record = {}
|
|
109
|
+
const encrypted = record.encrypted_content
|
|
110
|
+
Object.assign(record, structuredClone(event.item))
|
|
111
|
+
if (typeof record.encrypted_content !== 'string' && typeof encrypted === 'string') record.encrypted_content = encrypted
|
|
112
|
+
if (index !== undefined) byIndex.set(index, record)
|
|
113
|
+
if (id) byId.set(id, record)
|
|
114
|
+
}
|
|
115
|
+
await consumeSse(response, (event) => {
|
|
116
|
+
if (!isObject(event)) return
|
|
117
|
+
if (event.type === 'error' || event.type === 'response.failed' || event.type === 'response.incomplete') {
|
|
118
|
+
const upstream = event.type === 'response.failed' ? event.response?.error : event.error
|
|
119
|
+
const providerCode = safeMachineField(upstream?.code)
|
|
120
|
+
const providerType = safeMachineField(upstream?.type)
|
|
121
|
+
const providerParam = safeMachineField(upstream?.param)
|
|
122
|
+
const diagnostics = [
|
|
123
|
+
providerCode ? `providerCode=${providerCode}` : '',
|
|
124
|
+
providerType ? `providerType=${providerType}` : '',
|
|
125
|
+
providerParam ? `providerParam=${providerParam}` : '',
|
|
126
|
+
].filter(Boolean).join(' ')
|
|
127
|
+
const e = fail(`native compaction ended with ${event.type}${diagnostics ? ` ${diagnostics}` : ''}`, 'LCX_COMPACT_UPSTREAM_ERROR')
|
|
128
|
+
if (Number.isInteger(upstream?.status ?? event.error?.status)) e.status = upstream?.status ?? event.error?.status
|
|
129
|
+
if (providerCode) e.providerCode = providerCode
|
|
130
|
+
if (providerType) e.providerType = providerType
|
|
131
|
+
if (providerParam) e.providerParam = providerParam
|
|
132
|
+
throw e
|
|
245
133
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (completedResponse.object !== undefined && !['response.compaction', 'response'].includes(completedResponse.object)) {
|
|
256
|
-
throw protocolError(`Native compaction returned unexpected response object: ${String(completedResponse.object)}`, 'LCX_COMPACT_INVALID_RESPONSE')
|
|
257
|
-
}
|
|
258
|
-
const mergedEventOutput = eventItems.map((record) => record.item)
|
|
259
|
-
const output = validCompletedOutput(completedOutput) ? completedOutput : mergedEventOutput
|
|
260
|
-
const normalized = normalizeCompactionResponse({
|
|
261
|
-
object: completedResponse.object ?? 'response.compaction',
|
|
262
|
-
id: typeof completedResponse.id === 'string' ? completedResponse.id : undefined,
|
|
263
|
-
output,
|
|
264
|
-
})
|
|
134
|
+
if (event.type === 'response.output_item.added' || event.type === 'response.output_item.done') mergeItem(event)
|
|
135
|
+
if (event.type === 'response.completed') terminal = event.response
|
|
136
|
+
}, options)
|
|
137
|
+
if (!isObject(terminal)) throw fail('native compaction stream ended without response.completed', 'LCX_COMPACT_INCOMPLETE_SSE')
|
|
138
|
+
if (terminal.status !== 'completed') throw fail('native compaction response.completed did not carry completed status', 'LCX_COMPACT_INCOMPLETE_SSE')
|
|
139
|
+
const eventItems = [...new Set([...byIndex.values(), ...byId.values()])]
|
|
140
|
+
const output = Array.isArray(terminal.output) && terminal.output.length > 0 ? terminal.output : eventItems
|
|
141
|
+
const normalized = validateOutput(output)
|
|
265
142
|
return {
|
|
143
|
+
object: terminal.object ?? 'response.compaction',
|
|
144
|
+
...(typeof terminal.id === 'string' ? { id: terminal.id } : {}),
|
|
266
145
|
...normalized,
|
|
267
|
-
usage: usageFrom(
|
|
146
|
+
usage: usageFrom(terminal.usage),
|
|
268
147
|
}
|
|
269
148
|
}
|
|
270
149
|
|
|
271
|
-
export async function requestNativeCompaction({
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
tools,
|
|
280
|
-
headers,
|
|
281
|
-
signal,
|
|
282
|
-
timeoutMs,
|
|
283
|
-
maxAttempts,
|
|
284
|
-
maxResponseBytes,
|
|
285
|
-
}) {
|
|
286
|
-
const body = buildNativeCompactionBody({ model, input, instructions, promptCacheKey, promptCacheRetention, tools })
|
|
287
|
-
const requestHeaders = { ...(headers ?? {}) }
|
|
288
|
-
if (idempotencyKey !== undefined && idempotencyKey !== null && !Object.keys(requestHeaders).some((name) => name.toLowerCase() === 'idempotency-key')) {
|
|
289
|
-
requestHeaders['idempotency-key'] = String(idempotencyKey)
|
|
290
|
-
}
|
|
291
|
-
const featureHeaders = mergeFeatureHeader(requestHeaders)
|
|
292
|
-
return fetchSseWithRetry(
|
|
293
|
-
`${String(baseURL).replace(/\/+$/u, '')}/responses`,
|
|
294
|
-
body,
|
|
295
|
-
featureHeaders,
|
|
296
|
-
signal,
|
|
297
|
-
timeoutMs,
|
|
298
|
-
{
|
|
299
|
-
maxAttempts,
|
|
300
|
-
maxResponseBytes,
|
|
301
|
-
consume: (response, consumeOptions = {}) => parseNativeCompactionSse(response, {
|
|
302
|
-
signal: consumeOptions.requestSignal ?? signal,
|
|
303
|
-
maxResponseBytes,
|
|
304
|
-
}),
|
|
305
|
-
},
|
|
306
|
-
)
|
|
150
|
+
export async function requestNativeCompaction({ baseURL, model, input, instructions, tools, promptCacheKey, promptCacheRetention, reasoningEffort, temperature, maxTokens, idempotencyKey, headers, signal, timeoutMs, maxAttempts, maxResponseBytes }) {
|
|
151
|
+
const body = buildNativeCompactionBody({ model, input, instructions, tools, promptCacheKey, promptCacheRetention, reasoningEffort, temperature, maxTokens })
|
|
152
|
+
const requestHeaders = mergeFeatureHeader({ ...headers, ...(idempotencyKey ? { 'idempotency-key': String(idempotencyKey) } : {}) })
|
|
153
|
+
return fetchSseWithRetry(`${String(baseURL).replace(/\/+$/u, '')}/responses`, body, requestHeaders, signal, timeoutMs, {
|
|
154
|
+
maxAttempts,
|
|
155
|
+
maxResponseBytes,
|
|
156
|
+
consume: (response, consumeOptions) => parseNativeCompactionSse(response, { signal: consumeOptions.requestSignal ?? signal, maxResponseBytes }),
|
|
157
|
+
})
|
|
307
158
|
}
|