@jeffreycao/copilot-api 1.9.15 → 1.10.0
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/README.md +99 -82
- package/README.zh-CN.md +101 -84
- package/dist/{auth-DEUCqXMV.js → auth-D7YCTWpx.js} +3 -3
- package/dist/{auth-DEUCqXMV.js.map → auth-D7YCTWpx.js.map} +1 -1
- package/dist/{check-usage-OcW_3tEg.js → check-usage-Dgg0nNEw.js} +3 -3
- package/dist/{check-usage-OcW_3tEg.js.map → check-usage-Dgg0nNEw.js.map} +1 -1
- package/dist/main.js +3 -3
- package/dist/{config-DrfmMOO-.js → proxy-De0Po8kG.js} +62 -3
- package/dist/proxy-De0Po8kG.js.map +1 -0
- package/dist/{server-DkUKa2I6.js → server-BnKth1Jp.js} +402 -80
- package/dist/server-BnKth1Jp.js.map +1 -0
- package/dist/{start-DFJynp4A.js → start-1KA2mHVS.js} +5 -52
- package/dist/start-1KA2mHVS.js.map +1 -0
- package/dist/{token-BOwQe3TO.js → token-BQlDdqtI.js} +2 -2
- package/dist/{token-BOwQe3TO.js.map → token-BQlDdqtI.js.map} +1 -1
- package/dist/{utils-Cj-ToKA6.js → utils-C5ej0z8n.js} +47 -7
- package/dist/utils-C5ej0z8n.js.map +1 -0
- package/package.json +1 -1
- package/dist/config-DrfmMOO-.js.map +0 -1
- package/dist/server-DkUKa2I6.js.map +0 -1
- package/dist/start-DFJynp4A.js.map +0 -1
- package/dist/utils-Cj-ToKA6.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-De0Po8kG.js","names":["get"],"sources":["../src/lib/config.ts","../src/lib/proxy.ts"],"sourcesContent":["import consola from \"consola\"\nimport fs from \"node:fs\"\n\nimport { PATHS } from \"./paths\"\n\nexport interface AppConfig {\n auth?: {\n apiKeys?: Array<string>\n }\n providers?: Record<string, ProviderConfig>\n extraPrompts?: Record<string, string>\n smallModel?: string\n responsesApiContextManagementModels?: Array<string>\n modelReasoningEfforts?: Record<\n string,\n \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"\n >\n useFunctionApplyPatch?: boolean\n useMessagesApi?: boolean\n useResponsesApiWebSocket?: boolean\n anthropicApiKey?: string\n useResponsesApiWebSearch?: boolean\n claudeTokenMultiplier?: number\n}\n\nexport interface ModelConfig {\n temperature?: number\n topP?: number\n topK?: number\n extraBody?: Record<string, unknown>\n contextCache?: boolean\n supportPdf?: boolean\n toolContentSupportType?: Array<ToolContentSupportType>\n}\n\nexport type ProviderAuthType = \"authorization\" | \"x-api-key\"\nexport type ProviderType = \"anthropic\" | \"openai-compatible\"\nexport type ToolContentSupportType = \"array\" | \"image\" | \"pdf\"\n\nexport interface ProviderConfig {\n type?: string\n enabled?: boolean\n baseUrl?: string\n apiKey?: string\n authType?: ProviderAuthType\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nexport interface ResolvedProviderConfig {\n name: string\n type: ProviderType\n baseUrl: string\n apiKey: string\n authType: ProviderAuthType\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nconst gpt5ExplorationPrompt = `## Exploration and reading files\n- **Think first.** Before any tool call, decide ALL files/resources you will need.\n- **Batch everything.** If you need multiple files (even from different places), read them together.\n- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.\n- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**\n- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`\n\nconst gpt5CommentaryPrompt = `# Working with the user\n\nYou interact with the user through a terminal. You have 2 ways of communicating with the users: \n- Share intermediary updates in \\`commentary\\` channel. \n- After you have completed all your work, send a message to the \\`final\\` channel. \n\n## Intermediary updates\n\n- Intermediary updates go to the \\`commentary\\` channel.\n- User updates are short updates while you are working, they are NOT final answers.\n- You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.\n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.\n- You provide user updates frequently, every 20s.\n- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as \"Got it -\" or \"Understood -\" etc.\n- When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.\n- After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).\n- Before performing file edits of any kind, you provide updates explaining what edits you are making.\n- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.\n- Tone of your updates MUST match your personality.`\n\nconst defaultConfig: AppConfig = {\n auth: {\n apiKeys: [],\n },\n providers: {},\n extraPrompts: {\n \"gpt-5-mini\": gpt5ExplorationPrompt,\n \"gpt-5.3-codex\": gpt5CommentaryPrompt,\n \"gpt-5.4-mini\": gpt5CommentaryPrompt,\n \"gpt-5.4\": gpt5CommentaryPrompt,\n \"gpt-5.5\": gpt5CommentaryPrompt,\n },\n smallModel: \"gpt-5-mini\",\n responsesApiContextManagementModels: [],\n modelReasoningEfforts: {\n \"gpt-5-mini\": \"low\",\n \"gpt-5.3-codex\": \"xhigh\",\n \"gpt-5.4-mini\": \"xhigh\",\n \"gpt-5.4\": \"xhigh\",\n \"gpt-5.5\": \"xhigh\",\n },\n useFunctionApplyPatch: true,\n useMessagesApi: true,\n useResponsesApiWebSocket: true,\n useResponsesApiWebSearch: true,\n}\n\nlet cachedConfig: AppConfig | null = null\n\nfunction ensureConfigFile(): void {\n try {\n fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK)\n } catch {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n try {\n fs.chmodSync(PATHS.CONFIG_PATH, 0o600)\n } catch {\n return\n }\n }\n}\n\nfunction readConfigFromDisk(): AppConfig {\n ensureConfigFile()\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n return defaultConfig\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n consola.error(\"Failed to read config file, using default config\", error)\n return defaultConfig\n }\n}\n\nfunction mergeDefaultConfig(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const extraPrompts = config.extraPrompts ?? {}\n const defaultExtraPrompts = defaultConfig.extraPrompts ?? {}\n const modelReasoningEfforts = config.modelReasoningEfforts ?? {}\n const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {}\n\n const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter(\n (model) => !Object.hasOwn(extraPrompts, model),\n )\n\n const missingReasoningEffortModels = Object.keys(\n defaultModelReasoningEfforts,\n ).filter((model) => !Object.hasOwn(modelReasoningEfforts, model))\n\n const hasExtraPromptChanges = missingExtraPromptModels.length > 0\n const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0\n\n if (!hasExtraPromptChanges && !hasReasoningEffortChanges) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n extraPrompts: {\n ...defaultExtraPrompts,\n ...extraPrompts,\n },\n modelReasoningEfforts: {\n ...defaultModelReasoningEfforts,\n ...modelReasoningEfforts,\n },\n },\n changed: true,\n }\n}\n\nexport function mergeConfigWithDefaults(): AppConfig {\n const config = readConfigFromDisk()\n const { mergedConfig, changed } = mergeDefaultConfig(config)\n\n if (changed) {\n try {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(mergedConfig, null, 2)}\\n`,\n \"utf8\",\n )\n } catch (writeError) {\n consola.warn(\n \"Failed to write merged extraPrompts to config file\",\n writeError,\n )\n }\n }\n\n cachedConfig = mergedConfig\n return mergedConfig\n}\n\nexport function getConfig(): AppConfig {\n cachedConfig ??= mergeDefaultConfig(readConfigFromDisk()).mergedConfig\n return cachedConfig\n}\n\nexport function getExtraPromptForModel(model: string): string {\n const config = getConfig()\n return config.extraPrompts?.[model] ?? \"\"\n}\n\nexport function getSmallModel(): string {\n const config = getConfig()\n return config.smallModel ?? \"gpt-5-mini\"\n}\n\nexport function getResponsesApiContextManagementModels(): Array<string> {\n const config = getConfig()\n return (\n config.responsesApiContextManagementModels\n ?? defaultConfig.responsesApiContextManagementModels\n ?? []\n )\n}\n\nexport function isResponsesApiContextManagementModel(model: string): boolean {\n return getResponsesApiContextManagementModels().includes(model)\n}\n\nexport function getReasoningEffortForModel(\n model: string,\n): \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" {\n const config = getConfig()\n return config.modelReasoningEfforts?.[model] ?? \"high\"\n}\n\nexport function normalizeProviderBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/u, \"\")\n}\n\nfunction getDefaultProviderAuthType(\n providerType: ProviderType,\n): ProviderAuthType {\n return providerType === \"openai-compatible\" ? \"authorization\" : \"x-api-key\"\n}\n\nexport function resolveProviderAuthType(\n providerName: string,\n authType: string | undefined,\n providerType: ProviderType,\n): ProviderAuthType {\n if (authType === undefined) {\n return getDefaultProviderAuthType(providerType)\n }\n\n if (authType === \"x-api-key\") {\n return \"x-api-key\"\n }\n\n if (authType === \"authorization\") {\n return authType\n }\n\n consola.warn(\n `Provider ${providerName} has invalid authType '${authType}', falling back to ${getDefaultProviderAuthType(providerType)}`,\n )\n return getDefaultProviderAuthType(providerType)\n}\n\nexport function getProviderConfig(name: string): ResolvedProviderConfig | null {\n const providerName = name.trim()\n if (!providerName) {\n return null\n }\n\n const config = getConfig()\n const provider = config.providers?.[providerName]\n if (!provider) {\n return null\n }\n\n if (provider.enabled === false) {\n return null\n }\n\n const type = provider.type ?? \"anthropic\"\n if (type !== \"anthropic\" && type !== \"openai-compatible\") {\n consola.warn(\n `Provider ${providerName} is ignored because type '${type}' is not supported`,\n )\n return null\n }\n\n const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? \"\")\n const apiKey = (provider.apiKey ?? \"\").trim()\n const authType = resolveProviderAuthType(\n providerName,\n provider.authType,\n type,\n )\n if (!baseUrl || !apiKey) {\n consola.warn(\n `Provider ${providerName} is enabled but missing baseUrl or apiKey`,\n )\n return null\n }\n\n return {\n name: providerName,\n type,\n baseUrl,\n apiKey,\n authType,\n models: provider.models,\n adjustInputTokens: provider.adjustInputTokens,\n }\n}\n\nexport function listEnabledProviders(): Array<string> {\n const config = getConfig()\n const providerNames = Object.keys(config.providers ?? {})\n return providerNames.filter((name) => getProviderConfig(name) !== null)\n}\n\nexport function isMessagesApiEnabled(): boolean {\n const config = getConfig()\n return config.useMessagesApi ?? true\n}\n\nexport function isResponsesApiWebSocketEnabled(): boolean {\n const config = getConfig()\n return config.useResponsesApiWebSocket ?? true\n}\n\nexport function getAnthropicApiKey(): string | undefined {\n const config = getConfig()\n return config.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY ?? undefined\n}\n\nexport function isResponsesApiWebSearchEnabled(): boolean {\n const config = getConfig()\n return config.useResponsesApiWebSearch ?? true\n}\n\nexport function getClaudeTokenMultiplier(): number {\n const config = getConfig()\n return config.claudeTokenMultiplier ?? 1.15\n}\n","import consola from \"consola\"\nimport { getProxyForUrl } from \"proxy-from-env\"\nimport { Agent, ProxyAgent, setGlobalDispatcher, type Dispatcher } from \"undici\"\n\nlet proxyEnvDispatcher: Dispatcher | undefined\n\nexport function getProxyEnvDispatcher(): Dispatcher | undefined {\n return proxyEnvDispatcher\n}\n\nexport function initProxyFromEnv(): void {\n try {\n const direct = new Agent()\n const proxies = new Map<string, ProxyAgent>()\n\n // We only need a minimal dispatcher that implements `dispatch` at runtime.\n // Typing the object as `Dispatcher` forces TypeScript to require many\n // additional methods. Instead, keep a plain object and cast when passing\n // to `setGlobalDispatcher`.\n const dispatcher = {\n dispatch(\n options: Dispatcher.DispatchOptions,\n handler: Dispatcher.DispatchHandler,\n ) {\n try {\n const origin =\n typeof options.origin === \"string\" ?\n new URL(options.origin)\n : (options.origin as URL)\n const get = getProxyForUrl as unknown as (\n u: string,\n ) => string | undefined\n const raw = get(origin.toString())\n const proxyUrl = raw && raw.length > 0 ? raw : undefined\n if (!proxyUrl) {\n consola.debug(`HTTP proxy bypass: ${origin.hostname}`)\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n let agent = proxies.get(proxyUrl)\n if (!agent) {\n agent = new ProxyAgent(proxyUrl)\n proxies.set(proxyUrl, agent)\n }\n let label = proxyUrl\n try {\n const u = new URL(proxyUrl)\n label = `${u.protocol}//${u.host}`\n } catch {\n /* noop */\n }\n consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`)\n return (agent as unknown as Dispatcher).dispatch(options, handler)\n } catch {\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n },\n close() {\n return direct.close()\n },\n destroy() {\n return direct.destroy()\n },\n }\n\n proxyEnvDispatcher = dispatcher as unknown as Dispatcher\n\n if (typeof Bun !== \"undefined\") {\n consola.debug(\"WebSocket proxy configured from environment (per-URL)\")\n return\n }\n\n setGlobalDispatcher(proxyEnvDispatcher)\n consola.debug(\"HTTP proxy configured from environment (per-URL)\")\n } catch (err) {\n consola.debug(\"Proxy setup skipped:\", err)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as PATHS } from "./paths-DC-mqCY3.js";
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
2
|
+
import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-C5ej0z8n.js";
|
|
3
|
+
import { a as getConfig, c as getReasoningEffortForModel, d as isResponsesApiContextManagementModel, f as isResponsesApiWebSearchEnabled, i as getClaudeTokenMultiplier, l as getSmallModel, o as getExtraPromptForModel, p as isResponsesApiWebSocketEnabled, r as getAnthropicApiKey, s as getProviderConfig, t as getProxyEnvDispatcher, u as isMessagesApiEnabled } from "./proxy-De0Po8kG.js";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
7
8
|
import { Hono } from "hono";
|
|
8
9
|
import { cors } from "hono/cors";
|
|
9
10
|
import { logger } from "hono/logger";
|
|
@@ -11,6 +12,7 @@ import fs$1, { readFileSync } from "node:fs";
|
|
|
11
12
|
import { streamSSE } from "hono/streaming";
|
|
12
13
|
import util from "node:util";
|
|
13
14
|
import { events } from "fetch-event-stream";
|
|
15
|
+
import { WebSocket } from "undici";
|
|
14
16
|
//#region src/lib/request-auth.ts
|
|
15
17
|
function normalizeApiKeys(apiKeys) {
|
|
16
18
|
if (!Array.isArray(apiKeys)) {
|
|
@@ -831,10 +833,14 @@ const copilotRateLimitHeaders = {
|
|
|
831
833
|
session: "x-usage-ratelimit-session",
|
|
832
834
|
weekly: "x-usage-ratelimit-weekly"
|
|
833
835
|
};
|
|
836
|
+
const copilotQuotaSnapshotKeys = {
|
|
837
|
+
session: "5Hour-Session-RateLimits",
|
|
838
|
+
weekly: "Weekly-Session-RateLimits"
|
|
839
|
+
};
|
|
834
840
|
const hasGetMethod = (headers) => {
|
|
835
841
|
return "get" in headers && typeof headers.get === "function";
|
|
836
842
|
};
|
|
837
|
-
const getHeaderValue = (headers, headerName) => {
|
|
843
|
+
const getHeaderValue$1 = (headers, headerName) => {
|
|
838
844
|
if (hasGetMethod(headers)) return headers.get(headerName);
|
|
839
845
|
const normalizedHeaderName = headerName.toLowerCase();
|
|
840
846
|
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
|
|
@@ -851,7 +857,7 @@ const parseCopilotRateLimitHeader = (headerValue) => {
|
|
|
851
857
|
};
|
|
852
858
|
const getCopilotRateLimitUsage = (headers, type) => {
|
|
853
859
|
const headerName = copilotRateLimitHeaders[type];
|
|
854
|
-
const headerValue = getHeaderValue(headers, headerName);
|
|
860
|
+
const headerValue = getHeaderValue$1(headers, headerName);
|
|
855
861
|
if (!headerValue) return null;
|
|
856
862
|
const parsed = parseCopilotRateLimitHeader(headerValue);
|
|
857
863
|
if (!parsed) return null;
|
|
@@ -860,15 +866,39 @@ const getCopilotRateLimitUsage = (headers, type) => {
|
|
|
860
866
|
...parsed
|
|
861
867
|
};
|
|
862
868
|
};
|
|
869
|
+
const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
|
|
870
|
+
const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
|
|
871
|
+
if (!isCopilotQuotaSnapshot(snapshot)) return null;
|
|
872
|
+
return {
|
|
873
|
+
remaining: String(snapshot.percent_remaining),
|
|
874
|
+
resetAt: snapshot.reset_date,
|
|
875
|
+
type
|
|
876
|
+
};
|
|
877
|
+
};
|
|
863
878
|
const logCopilotRateLimits = (headers) => {
|
|
864
879
|
for (const type of copilotRateLimitTypes) {
|
|
865
880
|
const usage = getCopilotRateLimitUsage(headers, type);
|
|
866
881
|
if (!usage) continue;
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
882
|
+
logCopilotRateLimitUsage(usage);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
const logCopilotQuotaSnapshots = (snapshots) => {
|
|
886
|
+
for (const type of copilotRateLimitTypes) {
|
|
887
|
+
const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
|
|
888
|
+
if (!usage) continue;
|
|
889
|
+
logCopilotRateLimitUsage(usage);
|
|
870
890
|
}
|
|
871
891
|
};
|
|
892
|
+
const logCopilotRateLimitUsage = (usage) => {
|
|
893
|
+
const d = new Date(usage.resetAt);
|
|
894
|
+
const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
|
|
895
|
+
consola.info(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
|
|
896
|
+
};
|
|
897
|
+
const isCopilotQuotaSnapshot = (value) => {
|
|
898
|
+
if (!value || typeof value !== "object") return false;
|
|
899
|
+
const record = value;
|
|
900
|
+
return typeof record.entitlement === "string" && typeof record.percent_remaining === "number" && typeof record.overage_permitted === "boolean" && typeof record.overage_count === "number" && typeof record.reset_date === "string";
|
|
901
|
+
};
|
|
872
902
|
//#endregion
|
|
873
903
|
//#region src/services/copilot/create-chat-completions.ts
|
|
874
904
|
const createChatCompletions = async (payload, options) => {
|
|
@@ -2426,9 +2456,76 @@ const adjustInputTokens = (providerConfig, usage) => {
|
|
|
2426
2456
|
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
2427
2457
|
debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
|
|
2428
2458
|
};
|
|
2459
|
+
const responsesUtilsDependencies = {
|
|
2460
|
+
isResponsesApiContextManagementModel,
|
|
2461
|
+
isResponsesApiWebSocketEnabled
|
|
2462
|
+
};
|
|
2463
|
+
const getResponsesRequestOptions = (payload) => {
|
|
2464
|
+
return {
|
|
2465
|
+
vision: hasVisionInput(payload),
|
|
2466
|
+
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
2467
|
+
};
|
|
2468
|
+
};
|
|
2469
|
+
const getResponsesTransportForModel = (selectedModel, options = {}) => {
|
|
2470
|
+
const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
|
|
2471
|
+
const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
|
|
2472
|
+
if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
|
|
2473
|
+
if (supportedEndpoints.includes("/responses")) return "http";
|
|
2474
|
+
return null;
|
|
2475
|
+
};
|
|
2476
|
+
const hasAgentInitiator = (payload) => {
|
|
2477
|
+
const lastItem = getPayloadItems(payload).at(-1);
|
|
2478
|
+
if (!lastItem) return false;
|
|
2479
|
+
if (!("role" in lastItem) || !lastItem.role) return true;
|
|
2480
|
+
return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
|
|
2481
|
+
};
|
|
2482
|
+
const hasVisionInput = (payload) => {
|
|
2483
|
+
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
2484
|
+
};
|
|
2485
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
2486
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
|
|
2487
|
+
return 5e4;
|
|
2488
|
+
};
|
|
2489
|
+
const createCompactionContextManagement = (compactThreshold) => [{
|
|
2490
|
+
type: "compaction",
|
|
2491
|
+
compact_threshold: compactThreshold
|
|
2492
|
+
}];
|
|
2493
|
+
const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
|
|
2494
|
+
if (payload.context_management !== void 0) return;
|
|
2495
|
+
if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
|
|
2496
|
+
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
|
|
2497
|
+
};
|
|
2498
|
+
const compactInputByLatestCompaction = (payload) => {
|
|
2499
|
+
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
2500
|
+
const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
|
|
2501
|
+
if (latestCompactionMessageIndex === void 0) return;
|
|
2502
|
+
payload.input = payload.input.slice(latestCompactionMessageIndex);
|
|
2503
|
+
};
|
|
2504
|
+
const getLatestCompactionMessageIndex = (input) => {
|
|
2505
|
+
for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
|
|
2506
|
+
};
|
|
2507
|
+
const isCompactionInputItem = (value) => {
|
|
2508
|
+
return "type" in value && typeof value.type === "string" && value.type === "compaction";
|
|
2509
|
+
};
|
|
2510
|
+
const getPayloadItems = (payload) => {
|
|
2511
|
+
const result = [];
|
|
2512
|
+
const { input } = payload;
|
|
2513
|
+
if (Array.isArray(input)) result.push(...input);
|
|
2514
|
+
return result;
|
|
2515
|
+
};
|
|
2516
|
+
const containsVisionContent = (value) => {
|
|
2517
|
+
if (!value) return false;
|
|
2518
|
+
if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
|
|
2519
|
+
if (typeof value !== "object") return false;
|
|
2520
|
+
const record = value;
|
|
2521
|
+
if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
|
|
2522
|
+
if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
|
|
2523
|
+
return false;
|
|
2524
|
+
};
|
|
2429
2525
|
//#endregion
|
|
2430
2526
|
//#region src/services/copilot/create-responses.ts
|
|
2431
|
-
const
|
|
2527
|
+
const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
|
|
2528
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
2432
2529
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
2433
2530
|
const headers = {
|
|
2434
2531
|
...copilotHeaders(state, requestId, vision),
|
|
@@ -2438,6 +2535,17 @@ const createResponses = async (payload, { vision, initiator, subagentMarker, req
|
|
|
2438
2535
|
prepareForCompact(headers, compactType);
|
|
2439
2536
|
payload.service_tier = void 0;
|
|
2440
2537
|
consola.log(`<-- model: ${payload.model}`);
|
|
2538
|
+
if ((compactType === 1 ? "http" : transport) === "websocket") {
|
|
2539
|
+
const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
2540
|
+
requestId,
|
|
2541
|
+
subagentMarker
|
|
2542
|
+
}));
|
|
2543
|
+
if (payload.stream) return stream;
|
|
2544
|
+
return await consumeResponsesWebSocketStream(stream);
|
|
2545
|
+
}
|
|
2546
|
+
return await createHttpResponses(payload, headers);
|
|
2547
|
+
};
|
|
2548
|
+
const createHttpResponses = async (payload, headers) => {
|
|
2441
2549
|
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
2442
2550
|
method: "POST",
|
|
2443
2551
|
headers,
|
|
@@ -2451,6 +2559,264 @@ const createResponses = async (payload, { vision, initiator, subagentMarker, req
|
|
|
2451
2559
|
if (payload.stream) return events(response);
|
|
2452
2560
|
return await response.json();
|
|
2453
2561
|
};
|
|
2562
|
+
const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
|
|
2563
|
+
const initiator = getResponsesWebSocketInitiator(preparedHeaders);
|
|
2564
|
+
return {
|
|
2565
|
+
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
2566
|
+
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
2567
|
+
payload: buildResponsesWebSocketPayload(payload, initiator)
|
|
2568
|
+
};
|
|
2569
|
+
};
|
|
2570
|
+
const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
|
|
2571
|
+
const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
2572
|
+
const subagentKey = subagentMarker ? [
|
|
2573
|
+
subagentMarker.session_id,
|
|
2574
|
+
subagentMarker.agent_id,
|
|
2575
|
+
subagentMarker.agent_type
|
|
2576
|
+
].join(":") : "main";
|
|
2577
|
+
return [
|
|
2578
|
+
tokenFingerprint,
|
|
2579
|
+
payload.model,
|
|
2580
|
+
requestId,
|
|
2581
|
+
subagentKey
|
|
2582
|
+
].map(encodePoolKeyPart).join("|");
|
|
2583
|
+
};
|
|
2584
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
2585
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
2586
|
+
};
|
|
2587
|
+
const createPooledResponsesWebSocketStream = (request) => runResponsesWebSocketPoolRequest(request);
|
|
2588
|
+
const buildResponsesWebSocketPayload = (payload, initiator) => {
|
|
2589
|
+
const websocketPayload = {
|
|
2590
|
+
...payload,
|
|
2591
|
+
type: "response.create",
|
|
2592
|
+
initiator
|
|
2593
|
+
};
|
|
2594
|
+
delete websocketPayload.stream;
|
|
2595
|
+
delete websocketPayload["background"];
|
|
2596
|
+
delete websocketPayload.service_tier;
|
|
2597
|
+
return websocketPayload;
|
|
2598
|
+
};
|
|
2599
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
2600
|
+
const url = new URL(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
2601
|
+
if (url.protocol === "https:") url.protocol = "wss:";
|
|
2602
|
+
else if (url.protocol === "http:") url.protocol = "ws:";
|
|
2603
|
+
return url.toString();
|
|
2604
|
+
};
|
|
2605
|
+
const responsesWebSocketPool = /* @__PURE__ */ new Map();
|
|
2606
|
+
const runResponsesWebSocketPoolRequest = async function* (request) {
|
|
2607
|
+
const entry = getResponsesWebSocketPoolEntry(request);
|
|
2608
|
+
const release = await acquireResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2609
|
+
try {
|
|
2610
|
+
const websocket = await entry.websocketPromise;
|
|
2611
|
+
websocket.send(JSON.stringify(request.payload));
|
|
2612
|
+
for await (const data of createWebSocketMessageStream(websocket)) {
|
|
2613
|
+
const chunk = createResponsesWebSocketStreamChunk(data);
|
|
2614
|
+
yield chunk;
|
|
2615
|
+
if (isTerminalResponsesStreamChunk(chunk)) return;
|
|
2616
|
+
}
|
|
2617
|
+
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2618
|
+
throw new Error("Responses websocket ended without a terminal response");
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2621
|
+
throw toError(error);
|
|
2622
|
+
} finally {
|
|
2623
|
+
release();
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
const getResponsesWebSocketPoolEntry = (request) => {
|
|
2627
|
+
const existing = responsesWebSocketPool.get(request.poolKey);
|
|
2628
|
+
if (existing && !existing.closed) {
|
|
2629
|
+
clearResponsesWebSocketIdleTimer(existing);
|
|
2630
|
+
return existing;
|
|
2631
|
+
}
|
|
2632
|
+
const entry = createResponsesWebSocketPoolEntry(request);
|
|
2633
|
+
responsesWebSocketPool.set(request.poolKey, entry);
|
|
2634
|
+
return entry;
|
|
2635
|
+
};
|
|
2636
|
+
const createResponsesWebSocketPoolEntry = (request) => {
|
|
2637
|
+
const entry = {
|
|
2638
|
+
closed: false,
|
|
2639
|
+
idleTimer: null,
|
|
2640
|
+
lock: Promise.resolve(),
|
|
2641
|
+
websocketPromise: openResponsesWebSocket({
|
|
2642
|
+
headers: request.headers,
|
|
2643
|
+
url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
|
|
2644
|
+
})
|
|
2645
|
+
};
|
|
2646
|
+
entry.websocketPromise.then((websocket) => {
|
|
2647
|
+
websocket.addEventListener("close", () => {
|
|
2648
|
+
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2649
|
+
});
|
|
2650
|
+
websocket.addEventListener("error", () => {
|
|
2651
|
+
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2652
|
+
});
|
|
2653
|
+
}).catch(() => {
|
|
2654
|
+
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2655
|
+
});
|
|
2656
|
+
return entry;
|
|
2657
|
+
};
|
|
2658
|
+
const acquireResponsesWebSocketPoolEntry = async (poolKey, entry) => {
|
|
2659
|
+
clearResponsesWebSocketIdleTimer(entry);
|
|
2660
|
+
let releaseCurrent;
|
|
2661
|
+
const previousLock = entry.lock;
|
|
2662
|
+
entry.lock = new Promise((resolve) => {
|
|
2663
|
+
releaseCurrent = resolve;
|
|
2664
|
+
});
|
|
2665
|
+
await previousLock;
|
|
2666
|
+
clearResponsesWebSocketIdleTimer(entry);
|
|
2667
|
+
let released = false;
|
|
2668
|
+
return () => {
|
|
2669
|
+
if (released) return;
|
|
2670
|
+
released = true;
|
|
2671
|
+
releaseCurrent();
|
|
2672
|
+
if (!entry.closed) scheduleResponsesWebSocketIdleClose(poolKey, entry);
|
|
2673
|
+
};
|
|
2674
|
+
};
|
|
2675
|
+
const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
|
|
2676
|
+
clearResponsesWebSocketIdleTimer(entry);
|
|
2677
|
+
entry.idleTimer = setTimeout(() => {
|
|
2678
|
+
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2679
|
+
}, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
|
|
2680
|
+
unrefTimer(entry.idleTimer);
|
|
2681
|
+
};
|
|
2682
|
+
const clearResponsesWebSocketIdleTimer = (entry) => {
|
|
2683
|
+
if (entry.idleTimer) {
|
|
2684
|
+
clearTimeout(entry.idleTimer);
|
|
2685
|
+
entry.idleTimer = null;
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
|
|
2689
|
+
if (responsesWebSocketPool.get(poolKey) !== entry) return;
|
|
2690
|
+
responsesWebSocketPool.delete(poolKey);
|
|
2691
|
+
entry.closed = true;
|
|
2692
|
+
clearResponsesWebSocketIdleTimer(entry);
|
|
2693
|
+
entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
|
|
2694
|
+
};
|
|
2695
|
+
const unrefTimer = (timer) => {
|
|
2696
|
+
if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
|
|
2697
|
+
};
|
|
2698
|
+
const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
|
|
2699
|
+
const dispatcher = getProxyEnvDispatcher();
|
|
2700
|
+
const websocket = new WebSocket(url, dispatcher ? {
|
|
2701
|
+
dispatcher,
|
|
2702
|
+
headers
|
|
2703
|
+
} : { headers });
|
|
2704
|
+
const cleanup = () => {
|
|
2705
|
+
websocket.removeEventListener("open", onOpen);
|
|
2706
|
+
websocket.removeEventListener("error", onError);
|
|
2707
|
+
};
|
|
2708
|
+
const onOpen = () => {
|
|
2709
|
+
cleanup();
|
|
2710
|
+
resolve(websocket);
|
|
2711
|
+
};
|
|
2712
|
+
const onError = () => {
|
|
2713
|
+
cleanup();
|
|
2714
|
+
reject(/* @__PURE__ */ new Error("Failed to create responses websocket"));
|
|
2715
|
+
};
|
|
2716
|
+
websocket.addEventListener("open", onOpen);
|
|
2717
|
+
websocket.addEventListener("error", onError);
|
|
2718
|
+
});
|
|
2719
|
+
const createWebSocketMessageStream = async function* (websocket) {
|
|
2720
|
+
const queue = [];
|
|
2721
|
+
let closed = false;
|
|
2722
|
+
let error = null;
|
|
2723
|
+
let notify = null;
|
|
2724
|
+
const wake = () => {
|
|
2725
|
+
notify?.();
|
|
2726
|
+
notify = null;
|
|
2727
|
+
};
|
|
2728
|
+
const onMessage = (event) => {
|
|
2729
|
+
queue.push(normalizeWebSocketMessageData(event.data));
|
|
2730
|
+
wake();
|
|
2731
|
+
};
|
|
2732
|
+
const onClose = () => {
|
|
2733
|
+
closed = true;
|
|
2734
|
+
wake();
|
|
2735
|
+
};
|
|
2736
|
+
const onError = () => {
|
|
2737
|
+
error = /* @__PURE__ */ new Error("Responses websocket stream error");
|
|
2738
|
+
wake();
|
|
2739
|
+
};
|
|
2740
|
+
websocket.addEventListener("message", onMessage);
|
|
2741
|
+
websocket.addEventListener("close", onClose);
|
|
2742
|
+
websocket.addEventListener("error", onError);
|
|
2743
|
+
try {
|
|
2744
|
+
while (true) {
|
|
2745
|
+
const item = queue.shift();
|
|
2746
|
+
if (item) {
|
|
2747
|
+
yield await item;
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
if (error) throw toError(error);
|
|
2751
|
+
if (closed) break;
|
|
2752
|
+
await new Promise((resolve) => {
|
|
2753
|
+
notify = resolve;
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
} finally {
|
|
2757
|
+
websocket.removeEventListener("message", onMessage);
|
|
2758
|
+
websocket.removeEventListener("close", onClose);
|
|
2759
|
+
websocket.removeEventListener("error", onError);
|
|
2760
|
+
}
|
|
2761
|
+
};
|
|
2762
|
+
const normalizeWebSocketMessageData = async (data) => {
|
|
2763
|
+
if (typeof data === "string") return data;
|
|
2764
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
2765
|
+
if (ArrayBuffer.isView(data)) {
|
|
2766
|
+
const view = data;
|
|
2767
|
+
return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
|
|
2768
|
+
}
|
|
2769
|
+
if (isTextReadable(data)) return await data.text();
|
|
2770
|
+
return String(data);
|
|
2771
|
+
};
|
|
2772
|
+
const isTextReadable = (value) => {
|
|
2773
|
+
if (!value || typeof value !== "object" || !("text" in value)) return false;
|
|
2774
|
+
return typeof value.text === "function";
|
|
2775
|
+
};
|
|
2776
|
+
const toError = (value) => {
|
|
2777
|
+
if (value instanceof Error) return value;
|
|
2778
|
+
return new Error(String(value));
|
|
2779
|
+
};
|
|
2780
|
+
const getHeaderValue = (headers, headerName) => {
|
|
2781
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
2782
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
2783
|
+
};
|
|
2784
|
+
const encodePoolKeyPart = (value) => encodeURIComponent(value);
|
|
2785
|
+
const createResponsesWebSocketStreamChunk = (data) => {
|
|
2786
|
+
if (data === "[DONE]") return { data };
|
|
2787
|
+
try {
|
|
2788
|
+
const parsed = JSON.parse(data);
|
|
2789
|
+
if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
|
|
2790
|
+
return {
|
|
2791
|
+
data: JSON.stringify(parsed),
|
|
2792
|
+
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
2793
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
2794
|
+
};
|
|
2795
|
+
} catch {
|
|
2796
|
+
return { data };
|
|
2797
|
+
}
|
|
2798
|
+
};
|
|
2799
|
+
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
2800
|
+
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
2801
|
+
try {
|
|
2802
|
+
const parsed = JSON.parse(chunk.data);
|
|
2803
|
+
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
2804
|
+
} catch {
|
|
2805
|
+
return false;
|
|
2806
|
+
}
|
|
2807
|
+
};
|
|
2808
|
+
const consumeResponsesWebSocketStream = async (stream) => {
|
|
2809
|
+
for await (const chunk of stream) {
|
|
2810
|
+
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
2811
|
+
const event = JSON.parse(chunk.data);
|
|
2812
|
+
if (event.type === "error") throw new Error(event.message);
|
|
2813
|
+
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
|
|
2814
|
+
}
|
|
2815
|
+
throw new Error("Responses websocket ended without a terminal response");
|
|
2816
|
+
};
|
|
2817
|
+
const closeResponsesWebSocket = (websocket) => {
|
|
2818
|
+
if (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN) websocket.close();
|
|
2819
|
+
};
|
|
2454
2820
|
//#endregion
|
|
2455
2821
|
//#region src/routes/messages/responses-translation.ts
|
|
2456
2822
|
const MESSAGE_TYPE = "message";
|
|
@@ -3307,63 +3673,6 @@ const extractFunctionCallDetails = (rawEvent) => {
|
|
|
3307
3673
|
};
|
|
3308
3674
|
};
|
|
3309
3675
|
//#endregion
|
|
3310
|
-
//#region src/routes/responses/utils.ts
|
|
3311
|
-
const getResponsesRequestOptions = (payload) => {
|
|
3312
|
-
return {
|
|
3313
|
-
vision: hasVisionInput(payload),
|
|
3314
|
-
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
3315
|
-
};
|
|
3316
|
-
};
|
|
3317
|
-
const hasAgentInitiator = (payload) => {
|
|
3318
|
-
const lastItem = getPayloadItems(payload).at(-1);
|
|
3319
|
-
if (!lastItem) return false;
|
|
3320
|
-
if (!("role" in lastItem) || !lastItem.role) return true;
|
|
3321
|
-
return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
|
|
3322
|
-
};
|
|
3323
|
-
const hasVisionInput = (payload) => {
|
|
3324
|
-
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
3325
|
-
};
|
|
3326
|
-
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
3327
|
-
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
|
|
3328
|
-
return 5e4;
|
|
3329
|
-
};
|
|
3330
|
-
const createCompactionContextManagement = (compactThreshold) => [{
|
|
3331
|
-
type: "compaction",
|
|
3332
|
-
compact_threshold: compactThreshold
|
|
3333
|
-
}];
|
|
3334
|
-
const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
|
|
3335
|
-
if (payload.context_management !== void 0) return;
|
|
3336
|
-
if (!isResponsesApiContextManagementModel(payload.model)) return;
|
|
3337
|
-
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
|
|
3338
|
-
};
|
|
3339
|
-
const compactInputByLatestCompaction = (payload) => {
|
|
3340
|
-
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
3341
|
-
const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
|
|
3342
|
-
if (latestCompactionMessageIndex === void 0) return;
|
|
3343
|
-
payload.input = payload.input.slice(latestCompactionMessageIndex);
|
|
3344
|
-
};
|
|
3345
|
-
const getLatestCompactionMessageIndex = (input) => {
|
|
3346
|
-
for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
|
|
3347
|
-
};
|
|
3348
|
-
const isCompactionInputItem = (value) => {
|
|
3349
|
-
return "type" in value && typeof value.type === "string" && value.type === "compaction";
|
|
3350
|
-
};
|
|
3351
|
-
const getPayloadItems = (payload) => {
|
|
3352
|
-
const result = [];
|
|
3353
|
-
const { input } = payload;
|
|
3354
|
-
if (Array.isArray(input)) result.push(...input);
|
|
3355
|
-
return result;
|
|
3356
|
-
};
|
|
3357
|
-
const containsVisionContent = (value) => {
|
|
3358
|
-
if (!value) return false;
|
|
3359
|
-
if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
|
|
3360
|
-
if (typeof value !== "object") return false;
|
|
3361
|
-
const record = value;
|
|
3362
|
-
if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
|
|
3363
|
-
if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
|
|
3364
|
-
return false;
|
|
3365
|
-
};
|
|
3366
|
-
//#endregion
|
|
3367
3676
|
//#region src/services/copilot/create-messages.ts
|
|
3368
3677
|
const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
|
|
3369
3678
|
const allowedAnthropicBetas = new Set([
|
|
@@ -3657,6 +3966,11 @@ const prepareMessagesApiPayload = (payload, selectedModel) => {
|
|
|
3657
3966
|
const COPILOT_CONTEXT_CACHE_SYSTEM_MARKER_LIMIT = 2;
|
|
3658
3967
|
const COPILOT_CONTEXT_CACHE_NON_SYSTEM_MARKER_LIMIT = 2;
|
|
3659
3968
|
const COPILOT_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
3969
|
+
const messagesApiFlowDependencies = {
|
|
3970
|
+
createChatCompletions,
|
|
3971
|
+
createMessages,
|
|
3972
|
+
createResponses
|
|
3973
|
+
};
|
|
3660
3974
|
const handleWithChatCompletions = async (c, anthropicPayload, options) => {
|
|
3661
3975
|
const { logger, subagentMarker, requestId, sessionId, compactType } = options;
|
|
3662
3976
|
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
@@ -3668,7 +3982,7 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
|
|
|
3668
3982
|
payload: anthropicPayload
|
|
3669
3983
|
});
|
|
3670
3984
|
debugJson(logger, "Translated OpenAI request payload:", openAIPayload);
|
|
3671
|
-
const response = await createChatCompletions(openAIPayload, {
|
|
3985
|
+
const response = await messagesApiFlowDependencies.createChatCompletions(openAIPayload, {
|
|
3672
3986
|
subagentMarker,
|
|
3673
3987
|
requestId,
|
|
3674
3988
|
sessionId,
|
|
@@ -3731,9 +4045,11 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
|
|
|
3731
4045
|
compactInputByLatestCompaction(responsesPayload);
|
|
3732
4046
|
debugJson(logger, "Translated Responses payload:", responsesPayload);
|
|
3733
4047
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
3734
|
-
const
|
|
4048
|
+
const transport = getResponsesTransportForModel(selectedModel, { compactType: requestOptions.compactType }) ?? "http";
|
|
4049
|
+
const response = await messagesApiFlowDependencies.createResponses(responsesPayload, {
|
|
3735
4050
|
vision,
|
|
3736
4051
|
initiator,
|
|
4052
|
+
transport,
|
|
3737
4053
|
...requestOptions
|
|
3738
4054
|
});
|
|
3739
4055
|
if (responsesPayload.stream && isAsyncIterable$1(response)) {
|
|
@@ -3798,7 +4114,7 @@ const handleWithMessagesApi = async (c, anthropicPayload, options) => {
|
|
|
3798
4114
|
payload: anthropicPayload
|
|
3799
4115
|
});
|
|
3800
4116
|
debugJson(logger, "Translated Messages payload:", anthropicPayload);
|
|
3801
|
-
const response = await createMessages(anthropicPayload, anthropicBetaHeader, {
|
|
4117
|
+
const response = await messagesApiFlowDependencies.createMessages(anthropicPayload, anthropicBetaHeader, {
|
|
3802
4118
|
subagentMarker,
|
|
3803
4119
|
requestId,
|
|
3804
4120
|
sessionId,
|
|
@@ -3961,7 +4277,7 @@ async function handleCompletion(c) {
|
|
|
3961
4277
|
compactType,
|
|
3962
4278
|
logger: logger$3
|
|
3963
4279
|
});
|
|
3964
|
-
if (shouldUseResponsesApi(selectedModel)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
|
|
4280
|
+
if (shouldUseResponsesApi(selectedModel, compactType)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
|
|
3965
4281
|
subagentMarker,
|
|
3966
4282
|
selectedModel,
|
|
3967
4283
|
requestId,
|
|
@@ -3977,10 +4293,9 @@ async function handleCompletion(c) {
|
|
|
3977
4293
|
logger: logger$3
|
|
3978
4294
|
});
|
|
3979
4295
|
}
|
|
3980
|
-
const RESPONSES_ENDPOINT$1 = "/responses";
|
|
3981
4296
|
const MESSAGES_ENDPOINT = "/v1/messages";
|
|
3982
|
-
const shouldUseResponsesApi = (selectedModel) => {
|
|
3983
|
-
return selectedModel
|
|
4297
|
+
const shouldUseResponsesApi = (selectedModel, compactType) => {
|
|
4298
|
+
return Boolean(getResponsesTransportForModel(selectedModel, { compactType }));
|
|
3984
4299
|
};
|
|
3985
4300
|
const shouldUseMessagesApi = (selectedModel) => {
|
|
3986
4301
|
if (!isMessagesApiEnabled()) return false;
|
|
@@ -4113,9 +4428,14 @@ const handleItemId = (parsed, tracker) => {
|
|
|
4113
4428
|
//#endregion
|
|
4114
4429
|
//#region src/routes/responses/handler.ts
|
|
4115
4430
|
const logger$1 = createHandlerLogger("responses-handler");
|
|
4116
|
-
const
|
|
4431
|
+
const responsesHandlerDependencies = {
|
|
4432
|
+
checkRateLimit,
|
|
4433
|
+
createResponses,
|
|
4434
|
+
getConfig,
|
|
4435
|
+
isResponsesApiWebSearchEnabled
|
|
4436
|
+
};
|
|
4117
4437
|
const handleResponses = async (c) => {
|
|
4118
|
-
await checkRateLimit(state);
|
|
4438
|
+
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4119
4439
|
const payload = await c.req.json();
|
|
4120
4440
|
debugJson(logger$1, "Responses request payload:", payload);
|
|
4121
4441
|
const requestId = generateRequestIdFromPayload({ messages: payload.input });
|
|
@@ -4129,10 +4449,11 @@ const handleResponses = async (c) => {
|
|
|
4129
4449
|
});
|
|
4130
4450
|
useFunctionApplyPatch(payload);
|
|
4131
4451
|
removeUnsupportedTools(payload);
|
|
4132
|
-
if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
4452
|
+
if (!responsesHandlerDependencies.isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
4133
4453
|
compactInputByLatestCompaction(payload);
|
|
4134
4454
|
const selectedModel = state.models?.data.find((model) => model.id === payload.model);
|
|
4135
|
-
|
|
4455
|
+
const responsesTransport = getResponsesTransportForModel(selectedModel);
|
|
4456
|
+
if (!responsesTransport) return c.json({ error: {
|
|
4136
4457
|
message: "This model does not support the responses endpoint. Please choose a different model.",
|
|
4137
4458
|
type: "invalid_request_error"
|
|
4138
4459
|
} }, 400);
|
|
@@ -4140,11 +4461,12 @@ const handleResponses = async (c) => {
|
|
|
4140
4461
|
debugJson(logger$1, "Translated Responses payload:", payload);
|
|
4141
4462
|
const { vision, initiator } = getResponsesRequestOptions(payload);
|
|
4142
4463
|
if (state.manualApprove) await awaitApproval();
|
|
4143
|
-
const response = await createResponses(payload, {
|
|
4464
|
+
const response = await responsesHandlerDependencies.createResponses(payload, {
|
|
4144
4465
|
vision,
|
|
4145
4466
|
initiator,
|
|
4146
4467
|
requestId,
|
|
4147
|
-
sessionId
|
|
4468
|
+
sessionId,
|
|
4469
|
+
transport: responsesTransport
|
|
4148
4470
|
});
|
|
4149
4471
|
if (isStreamingRequested(payload) && isAsyncIterable(response)) {
|
|
4150
4472
|
logger$1.debug("Forwarding native Responses stream");
|
|
@@ -4184,7 +4506,7 @@ const parseResponsesStreamEvent = (chunk) => {
|
|
|
4184
4506
|
}
|
|
4185
4507
|
};
|
|
4186
4508
|
const useFunctionApplyPatch = (payload) => {
|
|
4187
|
-
if (getConfig().useFunctionApplyPatch ?? true) {
|
|
4509
|
+
if (responsesHandlerDependencies.getConfig().useFunctionApplyPatch ?? true) {
|
|
4188
4510
|
logger$1.debug("Using function tool apply_patch for responses");
|
|
4189
4511
|
if (Array.isArray(payload.tools)) {
|
|
4190
4512
|
const toolsArr = payload.tools;
|
|
@@ -4327,4 +4649,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
4327
4649
|
//#endregion
|
|
4328
4650
|
export { server };
|
|
4329
4651
|
|
|
4330
|
-
//# sourceMappingURL=server-
|
|
4652
|
+
//# sourceMappingURL=server-BnKth1Jp.js.map
|