@openhoo/hoopilot 2.1.1 → 2.1.3
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/cli.js +377 -60
- package/dist/cli.js.map +1 -1
- package/dist/index.js +344 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -94,6 +94,16 @@ function parseStreamingProxyMode(value) {
|
|
|
94
94
|
|
|
95
95
|
// src/openai.ts
|
|
96
96
|
var DEFAULT_MODEL = "gpt-4.1";
|
|
97
|
+
var COMPACTION_SUMMARIZATION_PROMPT = `You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.
|
|
98
|
+
|
|
99
|
+
Include:
|
|
100
|
+
- Current progress and key decisions made
|
|
101
|
+
- Important context, constraints, or user preferences
|
|
102
|
+
- What remains to be done (clear next steps)
|
|
103
|
+
- Any critical data, examples, or references needed to continue
|
|
104
|
+
|
|
105
|
+
Be concise, structured, and focused on helping the next LLM seamlessly continue the work.`;
|
|
106
|
+
var COMPACTION_SUMMARY_PREFIX = "Another language model started to solve this problem and produced a summary of its thinking process. You also have access to the state of the tools that were used by that language model. Use this to build on the work that has already been done and avoid duplicating work. Here is the summary produced by the other language model, use the information in this summary to assist with your own analysis:";
|
|
97
107
|
var OpenAICompatibilityError = class extends Error {
|
|
98
108
|
constructor(message) {
|
|
99
109
|
super(message);
|
|
@@ -184,19 +194,111 @@ function chatCompletionToResponse(completion, responseId) {
|
|
|
184
194
|
});
|
|
185
195
|
}
|
|
186
196
|
function responsesCompactionResult(upstreamText, isSse) {
|
|
187
|
-
const
|
|
188
|
-
return { output };
|
|
197
|
+
const summary = compactionSummaryText(upstreamText, isSse);
|
|
198
|
+
return { output: [compactionSummaryOutputMessageItem(summary)] };
|
|
199
|
+
}
|
|
200
|
+
function isResponsesCompactionRequest(request) {
|
|
201
|
+
return responseInputItems(request.input).some(
|
|
202
|
+
(item) => contentToText(asRecord(item).type) === "compaction_trigger"
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function responsesCompactionRequestBody(request) {
|
|
206
|
+
return JSON.stringify(
|
|
207
|
+
removeUndefined({
|
|
208
|
+
...request,
|
|
209
|
+
input: [
|
|
210
|
+
...compactionInputItemsForCopilot(request.input),
|
|
211
|
+
{
|
|
212
|
+
content: [{ text: COMPACTION_SUMMARIZATION_PROMPT, type: "input_text" }],
|
|
213
|
+
role: "user",
|
|
214
|
+
type: "message"
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
parallel_tool_calls: false,
|
|
218
|
+
stream: false,
|
|
219
|
+
tool_choice: "none",
|
|
220
|
+
tools: []
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
function normalizeResponsesRequestForCopilotBody(request) {
|
|
225
|
+
return JSON.stringify(
|
|
226
|
+
removeUndefined({
|
|
227
|
+
...request,
|
|
228
|
+
input: normalizeCompactionInputForCopilot(request.input, { dropTrigger: false })
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
function responsesRequestNeedsCopilotNormalization(request) {
|
|
233
|
+
return responseInputItems(request.input).some((item) => {
|
|
234
|
+
const type = contentToText(asRecord(item).type);
|
|
235
|
+
return type === "compaction" || type === "compaction_summary" || type === "context_compaction";
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function responsesCompactionResponse(upstreamText, isSse, model) {
|
|
239
|
+
const output = [compactionOutputItem(compactionSummaryText(upstreamText, isSse))];
|
|
240
|
+
return removeUndefined({
|
|
241
|
+
created_at: epochSeconds(),
|
|
242
|
+
error: null,
|
|
243
|
+
id: `resp_${randomId()}`,
|
|
244
|
+
incomplete_details: null,
|
|
245
|
+
instructions: null,
|
|
246
|
+
max_output_tokens: null,
|
|
247
|
+
metadata: {},
|
|
248
|
+
model,
|
|
249
|
+
object: "response",
|
|
250
|
+
output,
|
|
251
|
+
output_text: "",
|
|
252
|
+
parallel_tool_calls: false,
|
|
253
|
+
status: "completed",
|
|
254
|
+
temperature: null,
|
|
255
|
+
tool_choice: "none",
|
|
256
|
+
tools: [],
|
|
257
|
+
top_p: null
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function responsesCompactionSseText(upstreamText, isSse, model) {
|
|
261
|
+
const responseId = `resp_${randomId()}`;
|
|
262
|
+
const item = compactionOutputItem(compactionSummaryText(upstreamText, isSse));
|
|
263
|
+
const createdAt = epochSeconds();
|
|
264
|
+
let sequenceNumber = 0;
|
|
265
|
+
const event = (name, data) => encodeSse(name, data === "[DONE]" ? data : { ...data, sequence_number: sequenceNumber++ });
|
|
266
|
+
return [
|
|
267
|
+
event("response.created", {
|
|
268
|
+
response: baseStreamResponse(responseId, model, createdAt, "in_progress", []),
|
|
269
|
+
type: "response.created"
|
|
270
|
+
}),
|
|
271
|
+
event("response.output_item.done", {
|
|
272
|
+
item,
|
|
273
|
+
output_index: 0,
|
|
274
|
+
type: "response.output_item.done"
|
|
275
|
+
}),
|
|
276
|
+
event("response.completed", {
|
|
277
|
+
response: baseStreamResponse(responseId, model, createdAt, "completed", [item]),
|
|
278
|
+
type: "response.completed"
|
|
279
|
+
}),
|
|
280
|
+
event("done", "[DONE]")
|
|
281
|
+
].join("");
|
|
282
|
+
}
|
|
283
|
+
function compactionSummaryText(upstreamText, isSse) {
|
|
284
|
+
const summary = isSse ? compactionSummaryTextFromResponsesSse(upstreamText) : compactionSummaryTextFromResponse(asRecord(safeJsonParse(upstreamText)));
|
|
285
|
+
return summary.trim() || "(no summary available)";
|
|
189
286
|
}
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
287
|
+
function compactionSummaryTextFromResponse(response) {
|
|
288
|
+
const output = Array.isArray(response.output) ? response.output.map((item) => asRecord(item)) : [];
|
|
289
|
+
const compaction = output.find((item) => contentToText(item.type) === "compaction");
|
|
290
|
+
if (compaction) {
|
|
291
|
+
return contentToText(compaction.encrypted_content);
|
|
193
292
|
}
|
|
194
|
-
const text =
|
|
195
|
-
|
|
293
|
+
const text = outputText(output);
|
|
294
|
+
if (text) {
|
|
295
|
+
return text;
|
|
296
|
+
}
|
|
297
|
+
return contentToText(response.output_text);
|
|
196
298
|
}
|
|
197
|
-
function
|
|
299
|
+
function compactionSummaryTextFromResponsesSse(text) {
|
|
198
300
|
let deltas = "";
|
|
199
|
-
let
|
|
301
|
+
let completedResponse;
|
|
200
302
|
for (const block of text.split(/\r?\n\r?\n/)) {
|
|
201
303
|
const data = block.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join("");
|
|
202
304
|
if (!data || data === "[DONE]") {
|
|
@@ -207,16 +309,16 @@ function compactionOutputFromResponsesSse(text) {
|
|
|
207
309
|
if (type === "response.output_text.delta") {
|
|
208
310
|
deltas += contentToText(record.delta);
|
|
209
311
|
} else if (type === "response.completed" || type === "response.incomplete") {
|
|
210
|
-
|
|
211
|
-
if (Array.isArray(response.output)) {
|
|
212
|
-
completedOutput = response.output;
|
|
213
|
-
}
|
|
312
|
+
completedResponse = asRecord(record.response);
|
|
214
313
|
}
|
|
215
314
|
}
|
|
216
|
-
if (
|
|
217
|
-
|
|
315
|
+
if (completedResponse) {
|
|
316
|
+
const summary = compactionSummaryTextFromResponse(completedResponse);
|
|
317
|
+
if (summary) {
|
|
318
|
+
return summary;
|
|
319
|
+
}
|
|
218
320
|
}
|
|
219
|
-
return deltas
|
|
321
|
+
return deltas;
|
|
220
322
|
}
|
|
221
323
|
function chatCompletionToCompletion(completion) {
|
|
222
324
|
return removeUndefined({
|
|
@@ -737,6 +839,72 @@ function messageOutputItem(text, id = `msg_${randomId()}`) {
|
|
|
737
839
|
type: "message"
|
|
738
840
|
};
|
|
739
841
|
}
|
|
842
|
+
function compactionSummaryOutputMessageItem(text) {
|
|
843
|
+
return compactionSummaryMessageItem(text, `msg_${randomId()}`);
|
|
844
|
+
}
|
|
845
|
+
function compactionSummaryInputMessageItem(text) {
|
|
846
|
+
return compactionSummaryMessageItem(text);
|
|
847
|
+
}
|
|
848
|
+
function compactionSummaryMessageItem(text, id) {
|
|
849
|
+
return removeUndefined({
|
|
850
|
+
content: [
|
|
851
|
+
{
|
|
852
|
+
text: `${COMPACTION_SUMMARY_PREFIX}
|
|
853
|
+
${text}`,
|
|
854
|
+
type: "input_text"
|
|
855
|
+
}
|
|
856
|
+
],
|
|
857
|
+
id,
|
|
858
|
+
role: "user",
|
|
859
|
+
type: "message"
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
function compactionOutputItem(text, id = `cmpct_${randomId()}`) {
|
|
863
|
+
return {
|
|
864
|
+
encrypted_content: text,
|
|
865
|
+
id,
|
|
866
|
+
type: "compaction"
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function normalizeCompactionInputForCopilot(input, options) {
|
|
870
|
+
const items = responseInputItems(input);
|
|
871
|
+
if (items.length === 0) {
|
|
872
|
+
return input;
|
|
873
|
+
}
|
|
874
|
+
const normalized = [];
|
|
875
|
+
for (const item of items) {
|
|
876
|
+
const record = asRecord(item);
|
|
877
|
+
const type = contentToText(record.type);
|
|
878
|
+
if (type === "compaction_trigger" && options.dropTrigger) {
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (type === "compaction" || type === "compaction_summary" || type === "context_compaction") {
|
|
882
|
+
const text = contentToText(record.encrypted_content);
|
|
883
|
+
if (text) {
|
|
884
|
+
normalized.push(compactionSummaryInputMessageItem(text));
|
|
885
|
+
}
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
normalized.push(item);
|
|
889
|
+
}
|
|
890
|
+
return normalized;
|
|
891
|
+
}
|
|
892
|
+
function compactionInputItemsForCopilot(input) {
|
|
893
|
+
if (Array.isArray(input)) {
|
|
894
|
+
return normalizeCompactionInputForCopilot(input, { dropTrigger: true });
|
|
895
|
+
}
|
|
896
|
+
const text = contentToText(input);
|
|
897
|
+
return text ? [
|
|
898
|
+
{
|
|
899
|
+
content: [{ text, type: "input_text" }],
|
|
900
|
+
role: "user",
|
|
901
|
+
type: "message"
|
|
902
|
+
}
|
|
903
|
+
] : [];
|
|
904
|
+
}
|
|
905
|
+
function responseInputItems(input) {
|
|
906
|
+
return Array.isArray(input) ? input : [];
|
|
907
|
+
}
|
|
740
908
|
function functionCallItem(tool, status = "completed") {
|
|
741
909
|
return {
|
|
742
910
|
arguments: tool.arguments,
|
|
@@ -794,6 +962,7 @@ function extractTokenUsage(usage) {
|
|
|
794
962
|
asRecord(record.output_tokens_details).reasoning_tokens
|
|
795
963
|
);
|
|
796
964
|
const cached = firstNumber(
|
|
965
|
+
record.cache_read_input_tokens,
|
|
797
966
|
asRecord(record.prompt_tokens_details).cached_tokens,
|
|
798
967
|
asRecord(record.input_tokens_details).cached_tokens
|
|
799
968
|
);
|
|
@@ -966,9 +1135,10 @@ var AnthropicCompatibilityError = class extends Error {
|
|
|
966
1135
|
}
|
|
967
1136
|
};
|
|
968
1137
|
function anthropicMessagesToResponsesRequest(request) {
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1138
|
+
const system = anthropicSystemToResponses(request.system);
|
|
1139
|
+
const response = removeUndefined({
|
|
1140
|
+
input: [...system.input, ...anthropicMessagesToResponsesInput(request.messages)],
|
|
1141
|
+
instructions: system.instructions,
|
|
972
1142
|
max_output_tokens: typeof request.max_tokens === "number" && Number.isFinite(request.max_tokens) ? request.max_tokens : void 0,
|
|
973
1143
|
metadata: request.metadata,
|
|
974
1144
|
model: normalizeRequestedModel(request.model),
|
|
@@ -981,6 +1151,8 @@ function anthropicMessagesToResponsesRequest(request) {
|
|
|
981
1151
|
tools: anthropicTools(request.tools),
|
|
982
1152
|
top_p: request.top_p
|
|
983
1153
|
});
|
|
1154
|
+
applyCacheControlToLastBlock(response, anthropicCacheControl(request.cache_control));
|
|
1155
|
+
return response;
|
|
984
1156
|
}
|
|
985
1157
|
function responsesResponseToAnthropicMessage(response, fallbackModel) {
|
|
986
1158
|
const content = anthropicContentFromResponsesOutput(response);
|
|
@@ -1077,6 +1249,7 @@ function anthropicMessagesToResponsesInput(messages) {
|
|
|
1077
1249
|
throw new AnthropicCompatibilityError("Anthropic Messages requests require messages[].");
|
|
1078
1250
|
}
|
|
1079
1251
|
const input = [];
|
|
1252
|
+
let fallbackToolCallIndex = 0;
|
|
1080
1253
|
for (const message of messages) {
|
|
1081
1254
|
const record = asRecord(message);
|
|
1082
1255
|
const role = anthropicRole(record.role);
|
|
@@ -1098,10 +1271,13 @@ function anthropicMessagesToResponsesInput(messages) {
|
|
|
1098
1271
|
if (type === "text") {
|
|
1099
1272
|
const text = textValue(part.text);
|
|
1100
1273
|
if (text) {
|
|
1101
|
-
messageParts.push(
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1274
|
+
messageParts.push(
|
|
1275
|
+
removeUndefined({
|
|
1276
|
+
cache_control: anthropicCacheControl(part.cache_control),
|
|
1277
|
+
text,
|
|
1278
|
+
type: role === "assistant" ? "output_text" : "input_text"
|
|
1279
|
+
})
|
|
1280
|
+
);
|
|
1105
1281
|
}
|
|
1106
1282
|
continue;
|
|
1107
1283
|
}
|
|
@@ -1116,21 +1292,27 @@ function anthropicMessagesToResponsesInput(messages) {
|
|
|
1116
1292
|
}
|
|
1117
1293
|
if (type === "tool_use") {
|
|
1118
1294
|
flushMessage();
|
|
1119
|
-
input.push(
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1295
|
+
input.push(
|
|
1296
|
+
removeUndefined({
|
|
1297
|
+
arguments: JSON.stringify(asRecord(part.input)),
|
|
1298
|
+
cache_control: anthropicCacheControl(part.cache_control),
|
|
1299
|
+
call_id: textValue(part.id) || `call_hoopilot_${fallbackToolCallIndex++}`,
|
|
1300
|
+
name: textValue(part.name),
|
|
1301
|
+
type: "function_call"
|
|
1302
|
+
})
|
|
1303
|
+
);
|
|
1125
1304
|
continue;
|
|
1126
1305
|
}
|
|
1127
1306
|
if (type === "tool_result") {
|
|
1128
1307
|
flushMessage();
|
|
1129
|
-
input.push(
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1308
|
+
input.push(
|
|
1309
|
+
removeUndefined({
|
|
1310
|
+
cache_control: anthropicCacheControl(part.cache_control),
|
|
1311
|
+
call_id: textValue(part.tool_use_id),
|
|
1312
|
+
output: anthropicToolResultOutput(part.content),
|
|
1313
|
+
type: "function_call_output"
|
|
1314
|
+
})
|
|
1315
|
+
);
|
|
1134
1316
|
continue;
|
|
1135
1317
|
}
|
|
1136
1318
|
if (type === "thinking" || type === "redacted_thinking") {
|
|
@@ -1177,22 +1359,24 @@ function anthropicImageToResponsesPart(part) {
|
|
|
1177
1359
|
if (!data) {
|
|
1178
1360
|
throw new AnthropicCompatibilityError("Anthropic base64 image content requires source.data.");
|
|
1179
1361
|
}
|
|
1180
|
-
return {
|
|
1362
|
+
return removeUndefined({
|
|
1363
|
+
cache_control: anthropicCacheControl(part.cache_control),
|
|
1181
1364
|
detail: "auto",
|
|
1182
1365
|
image_url: `data:${mediaType};base64,${data}`,
|
|
1183
1366
|
type: "input_image"
|
|
1184
|
-
};
|
|
1367
|
+
});
|
|
1185
1368
|
}
|
|
1186
1369
|
if (sourceType === "url") {
|
|
1187
1370
|
const url = textValue(source.url);
|
|
1188
1371
|
if (!url) {
|
|
1189
1372
|
throw new AnthropicCompatibilityError("Anthropic URL image content requires source.url.");
|
|
1190
1373
|
}
|
|
1191
|
-
return {
|
|
1374
|
+
return removeUndefined({
|
|
1375
|
+
cache_control: anthropicCacheControl(part.cache_control),
|
|
1192
1376
|
detail: "auto",
|
|
1193
1377
|
image_url: url,
|
|
1194
1378
|
type: "input_image"
|
|
1195
|
-
};
|
|
1379
|
+
});
|
|
1196
1380
|
}
|
|
1197
1381
|
throw new AnthropicCompatibilityError(
|
|
1198
1382
|
`Anthropic image source type "${sourceType || "unknown"}" is not supported.`
|
|
@@ -1213,15 +1397,42 @@ function anthropicToolResultOutput(content) {
|
|
|
1213
1397
|
}
|
|
1214
1398
|
return typeof content === "object" ? JSON.stringify(content) : String(content);
|
|
1215
1399
|
}
|
|
1216
|
-
function
|
|
1400
|
+
function anthropicSystemToResponses(system) {
|
|
1217
1401
|
if (typeof system === "string") {
|
|
1218
|
-
return system || void 0;
|
|
1402
|
+
return { input: [], instructions: system || void 0 };
|
|
1219
1403
|
}
|
|
1220
1404
|
if (!Array.isArray(system)) {
|
|
1405
|
+
return { input: [] };
|
|
1406
|
+
}
|
|
1407
|
+
const parts = system.map((part) => anthropicSystemPartToResponsesPart(part)).filter((part) => part !== void 0);
|
|
1408
|
+
if (parts.length === 0) {
|
|
1409
|
+
return { input: [] };
|
|
1410
|
+
}
|
|
1411
|
+
if (parts.some((part) => part.cache_control !== void 0)) {
|
|
1412
|
+
return {
|
|
1413
|
+
input: [
|
|
1414
|
+
{
|
|
1415
|
+
content: parts,
|
|
1416
|
+
role: "system",
|
|
1417
|
+
type: "message"
|
|
1418
|
+
}
|
|
1419
|
+
]
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
const text = parts.map((part) => textValue(part.text)).filter(Boolean).join("\n");
|
|
1423
|
+
return { input: [], instructions: text || void 0 };
|
|
1424
|
+
}
|
|
1425
|
+
function anthropicSystemPartToResponsesPart(part) {
|
|
1426
|
+
const record = asRecord(part);
|
|
1427
|
+
const text = textValue(record.text) || textValue(part);
|
|
1428
|
+
if (!text) {
|
|
1221
1429
|
return void 0;
|
|
1222
1430
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1431
|
+
return removeUndefined({
|
|
1432
|
+
cache_control: anthropicCacheControl(record.cache_control),
|
|
1433
|
+
text,
|
|
1434
|
+
type: "input_text"
|
|
1435
|
+
});
|
|
1225
1436
|
}
|
|
1226
1437
|
function anthropicTools(tools) {
|
|
1227
1438
|
if (!Array.isArray(tools)) {
|
|
@@ -1230,6 +1441,7 @@ function anthropicTools(tools) {
|
|
|
1230
1441
|
const converted = tools.map((tool) => {
|
|
1231
1442
|
const record = asRecord(tool);
|
|
1232
1443
|
return removeUndefined({
|
|
1444
|
+
cache_control: anthropicCacheControl(record.cache_control),
|
|
1233
1445
|
description: record.description,
|
|
1234
1446
|
name: record.name,
|
|
1235
1447
|
parameters: record.input_schema,
|
|
@@ -1239,6 +1451,55 @@ function anthropicTools(tools) {
|
|
|
1239
1451
|
});
|
|
1240
1452
|
return converted.length > 0 ? converted : void 0;
|
|
1241
1453
|
}
|
|
1454
|
+
function anthropicCacheControl(value) {
|
|
1455
|
+
if (value === void 0 || value === null) {
|
|
1456
|
+
return void 0;
|
|
1457
|
+
}
|
|
1458
|
+
const record = asRecord(value);
|
|
1459
|
+
const type = textValue(record.type);
|
|
1460
|
+
if (type !== "ephemeral") {
|
|
1461
|
+
throw new AnthropicCompatibilityError(
|
|
1462
|
+
`Anthropic cache_control type "${type || "unknown"}" is not supported.`
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
const ttl = textValue(record.ttl);
|
|
1466
|
+
if (ttl && ttl !== "5m" && ttl !== "1h") {
|
|
1467
|
+
throw new AnthropicCompatibilityError(`Anthropic cache_control ttl "${ttl}" is not supported.`);
|
|
1468
|
+
}
|
|
1469
|
+
return removeUndefined({
|
|
1470
|
+
ttl: ttl || void 0,
|
|
1471
|
+
type
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
function applyCacheControlToLastBlock(request, cacheControl) {
|
|
1475
|
+
if (!cacheControl) {
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const input = Array.isArray(request.input) ? request.input : [];
|
|
1479
|
+
for (let itemIndex = input.length - 1; itemIndex >= 0; itemIndex -= 1) {
|
|
1480
|
+
const item = asRecord(input[itemIndex]);
|
|
1481
|
+
const content = Array.isArray(item.content) ? item.content : [];
|
|
1482
|
+
for (let partIndex = content.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
1483
|
+
const part = asRecord(content[partIndex]);
|
|
1484
|
+
if (part.cache_control === void 0 && isCacheableResponsesPart(part)) {
|
|
1485
|
+
part.cache_control = cacheControl;
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
const tools = Array.isArray(request.tools) ? request.tools : [];
|
|
1491
|
+
for (let index = tools.length - 1; index >= 0; index -= 1) {
|
|
1492
|
+
const tool = asRecord(tools[index]);
|
|
1493
|
+
if (tool.cache_control === void 0) {
|
|
1494
|
+
tool.cache_control = cacheControl;
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
function isCacheableResponsesPart(part) {
|
|
1500
|
+
const type = textValue(part.type);
|
|
1501
|
+
return type === "input_text" || type === "output_text" || type === "text" || type === "input_image";
|
|
1502
|
+
}
|
|
1242
1503
|
function anthropicToolChoice(toolChoice) {
|
|
1243
1504
|
if (toolChoice === void 0 || toolChoice === null) {
|
|
1244
1505
|
return void 0;
|
|
@@ -4080,7 +4341,21 @@ async function handleCompletions(client, metrics, recordTokens, recordExtraction
|
|
|
4080
4341
|
}
|
|
4081
4342
|
async function handleResponses(client, metrics, recordTokens, recordExtraction, request, logger, bufferProxyBodies) {
|
|
4082
4343
|
const { json, text: body } = await readJsonText(request);
|
|
4083
|
-
|
|
4344
|
+
if (isResponsesCompactionRequest(json)) {
|
|
4345
|
+
return handleResponsesCompactionV2(
|
|
4346
|
+
client,
|
|
4347
|
+
metrics,
|
|
4348
|
+
recordTokens,
|
|
4349
|
+
recordExtraction,
|
|
4350
|
+
json,
|
|
4351
|
+
request,
|
|
4352
|
+
logger
|
|
4353
|
+
);
|
|
4354
|
+
}
|
|
4355
|
+
const upstream = await client.responses(
|
|
4356
|
+
responsesRequestNeedsCopilotNormalization(json) ? normalizeResponsesRequestForCopilotBody(json) : body,
|
|
4357
|
+
request.signal
|
|
4358
|
+
);
|
|
4084
4359
|
metrics.recordUpstream("/responses", upstream.ok);
|
|
4085
4360
|
if (!upstream.ok) {
|
|
4086
4361
|
return proxyError(upstream, logger);
|
|
@@ -4100,10 +4375,7 @@ async function handleResponses(client, metrics, recordTokens, recordExtraction,
|
|
|
4100
4375
|
}
|
|
4101
4376
|
async function handleResponsesCompact(client, metrics, recordTokens, recordExtraction, request, logger) {
|
|
4102
4377
|
const body = await readJson(request);
|
|
4103
|
-
const upstream = await client.responses(
|
|
4104
|
-
JSON.stringify({ ...body, stream: false }),
|
|
4105
|
-
request.signal
|
|
4106
|
-
);
|
|
4378
|
+
const upstream = await client.responses(responsesCompactionRequestBody(body), request.signal);
|
|
4107
4379
|
metrics.recordUpstream("/responses", upstream.ok);
|
|
4108
4380
|
if (!upstream.ok) {
|
|
4109
4381
|
return proxyError(upstream, logger);
|
|
@@ -4120,6 +4392,22 @@ async function handleResponsesCompact(client, metrics, recordTokens, recordExtra
|
|
|
4120
4392
|
);
|
|
4121
4393
|
return jsonResponse(responsesCompactionResult(text, isSse));
|
|
4122
4394
|
}
|
|
4395
|
+
async function handleResponsesCompactionV2(client, metrics, recordTokens, recordExtraction, json, request, logger) {
|
|
4396
|
+
const upstream = await client.responses(responsesCompactionRequestBody(json), request.signal);
|
|
4397
|
+
metrics.recordUpstream("/responses", upstream.ok);
|
|
4398
|
+
if (!upstream.ok) {
|
|
4399
|
+
return proxyError(upstream, logger);
|
|
4400
|
+
}
|
|
4401
|
+
logUpstreamSuccess(logger, "/responses", upstream.status);
|
|
4402
|
+
const isSse = isStreamingResponse(upstream);
|
|
4403
|
+
const text = await upstream.text();
|
|
4404
|
+
const model = normalizeRequestedModel(json.model);
|
|
4405
|
+
recordResponseTextUsage(text, isSse, model, recordTokens, recordExtraction);
|
|
4406
|
+
if (json.stream === true) {
|
|
4407
|
+
return textResponse(responsesCompactionSseText(text, isSse, model), "text/event-stream");
|
|
4408
|
+
}
|
|
4409
|
+
return jsonResponse(responsesCompactionResponse(text, isSse, model));
|
|
4410
|
+
}
|
|
4123
4411
|
async function responseWithObservedUsage(response, fallbackModel, recordTokens, signal, bufferBody, recordExtraction) {
|
|
4124
4412
|
const isSse = isStreamingResponse(response);
|
|
4125
4413
|
if (bufferBody && response.body) {
|
|
@@ -4228,6 +4516,15 @@ function jsonResponse(body, status = 200) {
|
|
|
4228
4516
|
status
|
|
4229
4517
|
});
|
|
4230
4518
|
}
|
|
4519
|
+
function textResponse(body, contentType, status = 200) {
|
|
4520
|
+
return new Response(body, {
|
|
4521
|
+
headers: {
|
|
4522
|
+
...corsHeaders(),
|
|
4523
|
+
"content-type": `${contentType}; charset=utf-8`
|
|
4524
|
+
},
|
|
4525
|
+
status
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4231
4528
|
function jsonError(status, code, message) {
|
|
4232
4529
|
return jsonResponse(
|
|
4233
4530
|
{
|