@code-yeongyu/senpi-ai 2026.9.30 → 2026.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -19
- package/dist/api/anthropic-messages.js +168 -32
- package/dist/api/azure-openai-responses.js +23 -10
- package/dist/api/bedrock-converse-stream.js +12 -4
- package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
- package/dist/api/cloudflare-workers-ai-system-one.js +43 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
- package/dist/api/cloudflare.d.ts +2 -0
- package/dist/api/cloudflare.js +2 -0
- package/dist/api/context-room.d.ts +2 -2
- package/dist/api/cursor-agent.js +19 -10
- package/dist/api/devin-agent/request.d.ts +9 -9
- package/dist/api/devin-agent/request.js +14 -10
- package/dist/api/google-generative-ai.js +20 -80
- package/dist/api/google-shared.d.ts +13 -4
- package/dist/api/google-shared.js +54 -4
- package/dist/api/google-vertex.js +19 -62
- package/dist/api/llama-cpp-classify.d.ts +33 -0
- package/dist/api/llama-cpp-classify.js +365 -0
- package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
- package/dist/api/llama-cpp-classify.lazy.js +4 -0
- package/dist/api/mistral-conversations.d.ts +1 -1
- package/dist/api/mistral-conversations.js +36 -29
- package/dist/api/openai-codex-responses.d.ts +1 -1
- package/dist/api/openai-codex-responses.js +72 -37
- package/dist/api/openai-completions.d.ts +3 -2
- package/dist/api/openai-completions.js +76 -50
- package/dist/api/openai-images-params.d.ts +2 -2
- package/dist/api/openai-images.d.ts +1 -1
- package/dist/api/openai-responses-shared.d.ts +31 -8
- package/dist/api/openai-responses-shared.js +115 -27
- package/dist/api/openai-responses.d.ts +1 -1
- package/dist/api/openai-responses.js +38 -33
- package/dist/api/openrouter-images.d.ts +2 -1
- package/dist/api/openrouter-images.js +1 -0
- package/dist/api/pi-messages.d.ts +3 -3
- package/dist/api/pi-messages.js +3 -2
- package/dist/api/simple-options.d.ts +2 -2
- package/dist/api/simple-options.js +1 -0
- package/dist/api/system-one-shared.d.ts +23 -0
- package/dist/api/system-one-shared.js +183 -0
- package/dist/api/transform-messages.js +5 -2
- package/dist/api/typesafe-system-one.d.ts +4 -0
- package/dist/api/typesafe-system-one.js +19 -0
- package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
- package/dist/api/typesafe-system-one.lazy.js +4 -0
- package/dist/api-registry.d.ts +3 -3
- package/dist/auth/helpers.js +1 -1
- package/dist/auth/oauth/anthropic-callback-listener.js +1 -1
- package/dist/auth/oauth/callback-server.d.ts +55 -0
- package/dist/auth/oauth/callback-server.js +146 -0
- package/dist/auth/oauth/chatgpt-subscription.d.ts +1 -1
- package/dist/auth/oauth/chatgpt-subscription.js +20 -124
- package/dist/auth/oauth/devin-callback.js +1 -1
- package/dist/auth/oauth/load.d.ts +4 -0
- package/dist/auth/oauth/load.js +10 -0
- package/dist/auth/oauth/meta.d.ts +17 -0
- package/dist/auth/oauth/meta.js +190 -0
- package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
- package/dist/auth/oauth/openai-chatgpt.js +266 -0
- package/dist/auth/oauth/openrouter.d.ts +1 -1
- package/dist/auth/oauth/openrouter.js +19 -138
- package/dist/auth/oauth/radius.d.ts +1 -1
- package/dist/auth/oauth/radius.js +21 -89
- package/dist/auth/resolve.d.ts +3 -8
- package/dist/auth/resolve.js +3 -18
- package/dist/auth/types.d.ts +10 -1
- package/dist/bun-oauth.js +4 -0
- package/dist/cli.js +3 -1
- package/dist/compat.js +17 -14
- package/dist/env-api-keys.js +2 -0
- package/dist/image-models.d.ts +19 -8
- package/dist/image-models.js +14 -13
- package/dist/images-api-registry.d.ts +8 -8
- package/dist/images.d.ts +7 -2
- package/dist/images.js +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/model-catalog.d.ts +27 -9
- package/dist/model-catalog.js +24 -2
- package/dist/model.d.ts +13 -13
- package/dist/models-store.d.ts +3 -2
- package/dist/models.d.ts +106 -36
- package/dist/models.generated.d.ts +142 -42
- package/dist/models.generated.js +142 -42
- package/dist/models.js +174 -36
- package/dist/providers/alibaba-token-plan.models.d.ts +4 -2
- package/dist/providers/alibaba-token-plan.models.js +4 -2
- package/dist/providers/all.d.ts +72 -19
- package/dist/providers/all.js +28 -22
- package/dist/providers/amazon-bedrock.models.d.ts +4 -2
- package/dist/providers/amazon-bedrock.models.js +4 -2
- package/dist/providers/ant-ling.models.d.ts +4 -2
- package/dist/providers/ant-ling.models.js +4 -2
- package/dist/providers/anthropic.models.d.ts +4 -2
- package/dist/providers/anthropic.models.js +4 -2
- package/dist/providers/azure-openai-responses.models.d.ts +4 -2
- package/dist/providers/azure-openai-responses.models.js +4 -2
- package/dist/providers/bai.models.d.ts +4 -2
- package/dist/providers/bai.models.js +4 -2
- package/dist/providers/baseten.models.d.ts +4 -2
- package/dist/providers/baseten.models.js +4 -2
- package/dist/providers/cerebras.models.d.ts +4 -2
- package/dist/providers/cerebras.models.js +4 -2
- package/dist/providers/chatgpt-subscription.models.d.ts +4 -2
- package/dist/providers/chatgpt-subscription.models.js +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
- package/dist/providers/cloudflare-stream.d.ts +6 -2
- package/dist/providers/cloudflare-stream.js +6 -0
- package/dist/providers/cloudflare-workers-ai.js +10 -3
- package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
- package/dist/providers/cloudflare-workers-ai.models.js +4 -2
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/alibaba-token-plan.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/ant-ling.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/azure-openai-responses.json +1 -1
- package/dist/providers/data/bai.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cerebras.json +1 -1
- package/dist/providers/data/chatgpt-subscription.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/deepseek.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/groq.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/meta.json +1 -0
- package/dist/providers/data/minimax-cn.json +1 -1
- package/dist/providers/data/minimax.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/moonshotai-cn.json +1 -1
- package/dist/providers/data/moonshotai.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan-individual.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/radius.json +1 -0
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/typesafe.json +1 -0
- package/dist/providers/data/venice.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xai.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/dist/providers/data/xiaomi.json +1 -1
- package/dist/providers/data/zai-coding-cn.json +1 -1
- package/dist/providers/data/zai.json +1 -1
- package/dist/providers/deepseek.models.d.ts +4 -2
- package/dist/providers/deepseek.models.js +4 -2
- package/dist/providers/faux.d.ts +7 -2
- package/dist/providers/faux.js +31 -22
- package/dist/providers/fireworks.models.d.ts +4 -2
- package/dist/providers/fireworks.models.js +4 -2
- package/dist/providers/github-copilot.models.d.ts +4 -2
- package/dist/providers/github-copilot.models.js +4 -2
- package/dist/providers/google-vertex.models.d.ts +4 -2
- package/dist/providers/google-vertex.models.js +4 -2
- package/dist/providers/google.models.d.ts +4 -2
- package/dist/providers/google.models.js +4 -2
- package/dist/providers/groq.models.d.ts +4 -2
- package/dist/providers/groq.models.js +4 -2
- package/dist/providers/huggingface.models.d.ts +4 -2
- package/dist/providers/huggingface.models.js +4 -2
- package/dist/providers/images/register-builtins.d.ts +2 -2
- package/dist/providers/kimi-coding.models.d.ts +54 -7
- package/dist/providers/kimi-coding.models.js +34 -7
- package/dist/providers/meta.d.ts +3 -0
- package/dist/providers/meta.js +24 -0
- package/dist/providers/meta.models.d.ts +6 -0
- package/dist/providers/meta.models.js +8 -0
- package/dist/providers/minimax-cn.models.d.ts +4 -2
- package/dist/providers/minimax-cn.models.js +4 -2
- package/dist/providers/minimax.models.d.ts +4 -2
- package/dist/providers/minimax.models.js +4 -2
- package/dist/providers/mistral.models.d.ts +4 -2
- package/dist/providers/mistral.models.js +4 -2
- package/dist/providers/moonshotai-cn.models.d.ts +4 -2
- package/dist/providers/moonshotai-cn.models.js +4 -2
- package/dist/providers/moonshotai.models.d.ts +4 -2
- package/dist/providers/moonshotai.models.js +4 -2
- package/dist/providers/nvidia.models.d.ts +4 -2
- package/dist/providers/nvidia.models.js +4 -2
- package/dist/providers/openai.js +4 -2
- package/dist/providers/openai.models.d.ts +4 -2
- package/dist/providers/openai.models.js +4 -2
- package/dist/providers/opencode-go.models.d.ts +4 -2
- package/dist/providers/opencode-go.models.js +4 -2
- package/dist/providers/opencode.d.ts +3 -1
- package/dist/providers/opencode.js +5 -2
- package/dist/providers/opencode.models.d.ts +4 -2
- package/dist/providers/opencode.models.js +4 -2
- package/dist/providers/opengateway.models.d.ts +4 -2
- package/dist/providers/opengateway.models.js +4 -2
- package/dist/providers/openrouter.js +11 -2
- package/dist/providers/openrouter.models.d.ts +4 -2
- package/dist/providers/openrouter.models.js +4 -2
- package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan-cn.models.js +4 -2
- package/dist/providers/qwen-token-plan-individual.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan-individual.models.js +4 -2
- package/dist/providers/qwen-token-plan.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan.models.js +4 -2
- package/dist/providers/radius.js +19 -5
- package/dist/providers/radius.models.d.ts +6 -0
- package/dist/providers/radius.models.js +8 -0
- package/dist/providers/together.models.d.ts +4 -2
- package/dist/providers/together.models.js +4 -2
- package/dist/providers/typesafe.d.ts +3 -0
- package/dist/providers/typesafe.js +18 -0
- package/dist/providers/typesafe.models.d.ts +6 -0
- package/dist/providers/typesafe.models.js +8 -0
- package/dist/providers/venice.models.d.ts +4 -2
- package/dist/providers/venice.models.js +4 -2
- package/dist/providers/vercel-ai-gateway.js +5 -2
- package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
- package/dist/providers/vercel-ai-gateway.models.js +4 -2
- package/dist/providers/xai.models.d.ts +4 -2
- package/dist/providers/xai.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
- package/dist/providers/xiaomi.models.d.ts +4 -2
- package/dist/providers/xiaomi.models.js +4 -2
- package/dist/providers/zai-coding-cn.models.d.ts +4 -2
- package/dist/providers/zai-coding-cn.models.js +4 -2
- package/dist/providers/zai.models.d.ts +4 -2
- package/dist/providers/zai.models.js +4 -2
- package/dist/tool-call-middleware/context-transformer.d.ts +8 -5
- package/dist/tool-call-middleware/context-transformer.js +44 -15
- package/dist/types.d.ts +262 -28
- package/dist/utils/diagnostics.d.ts +3 -2
- package/dist/utils/estimate.d.ts +2 -2
- package/dist/utils/estimate.js +22 -30
- package/dist/utils/headers.d.ts +1 -1
- package/dist/utils/headers.js +10 -8
- package/dist/utils/model-operations.d.ts +11 -0
- package/dist/utils/model-operations.js +47 -0
- package/dist/utils/models-error.d.ts +8 -0
- package/dist/utils/models-error.js +18 -0
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.js +11 -5
- package/dist/utils/prompt-cache-ttl.js +10 -3
- package/dist/utils/retry.js +9 -0
- package/dist/utils/text.d.ts +9 -1
- package/dist/utils/text.js +26 -0
- package/dist/utils/transcript.d.ts +85 -0
- package/dist/utils/transcript.js +205 -0
- package/package.json +3 -4
- package/dist/image-models.generated.d.ts +0 -925
- package/dist/image-models.generated.js +0 -927
- package/dist/images-models.d.ts +0 -95
- package/dist/images-models.js +0 -141
- package/dist/providers/openai-images.d.ts +0 -3
- package/dist/providers/openai-images.js +0 -16
- package/dist/providers/openrouter-images.d.ts +0 -3
- package/dist/providers/openrouter-images.js +0 -22
- /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
- /package/dist/{auth/oauth → utils}/oauth-page.js +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { calculateCost, clampThinkingLevel, inferOpenAIThinkingLevelMap, supportsMax, supportsXhigh, } from "../models.js";
|
|
3
3
|
import { readProviderDiagnostic } from "../provider-diagnostic.js";
|
|
4
|
-
import { splitDeferredTools } from "../utils/deferred-tools.js";
|
|
5
4
|
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
6
5
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
7
6
|
import { formatGitHubCopilotToolLimitError, limitGitHubCopilotTools, recordGitHubCopilotToolLimit, } from "../utils/github-copilot-tool-limit.js";
|
|
@@ -11,6 +10,7 @@ import { awaitProviderTransport, openAICompatibleProviderDiagnosticFromError, }
|
|
|
11
10
|
import { getProviderEnvValue } from "../utils/provider-env.js";
|
|
12
11
|
import { retryProviderRequest } from "../utils/provider-retry.js";
|
|
13
12
|
import { sendWithForcedToolChoiceFallback } from "../utils/tool-choice-fallback.js";
|
|
13
|
+
import { getCurrentTools, getDeclaredTools, normalizeContext, resolveTranscript } from "../utils/transcript.js";
|
|
14
14
|
import { isCloudflareProvider, resolveCloudflareBaseUrl } from "./cloudflare.js";
|
|
15
15
|
import { createGrammarToolInputProperties } from "./constrained-sampling.js";
|
|
16
16
|
import { withGitHubCopilotFailureNote } from "./github-copilot-errors.js";
|
|
@@ -18,7 +18,7 @@ import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copi
|
|
|
18
18
|
import { resolveOpenAIClientAuth } from "./openai-client-auth.js";
|
|
19
19
|
import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
|
|
20
20
|
import { findPromptCacheComparisonResponseId, withPromptCacheComparison, } from "./openai-responses-prompt-cache.js";
|
|
21
|
-
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
21
|
+
import { convertResponsesMessages, convertResponsesTools, processResponsesStream, resolveResponsesDeferredToolsMode, resolveResponsesToolPlacement, } from "./openai-responses-shared.js";
|
|
22
22
|
import { buildBaseOptions, clampMaxForOpenAI, OPENAI_RESPONSES_RESERVED_BODY_KEYS } from "./simple-options.js";
|
|
23
23
|
import { startWebSocketLiveness } from "./websocket-liveness.js";
|
|
24
24
|
import { createWebSocketTransportFailure } from "./websocket-transport-failure.js";
|
|
@@ -29,6 +29,7 @@ const SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
|
29
29
|
// OpenAI Responses rejects max_output_tokens below 16: https://github.com/earendil-works/pi/issues/6265
|
|
30
30
|
const OPENAI_RESPONSES_MIN_OUTPUT_TOKENS = 16;
|
|
31
31
|
const PROMPT_CACHE_PREWARM_TIMEOUT_MS = 30_000;
|
|
32
|
+
const CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
|
|
32
33
|
const websocketSessionCache = new Map();
|
|
33
34
|
/** True when tool_choice forces a specific tool or mode (anything but "auto"/"none"). */
|
|
34
35
|
function isForcedOpenAIResponsesToolChoice(toolChoice) {
|
|
@@ -54,6 +55,7 @@ function getCompat(model, env) {
|
|
|
54
55
|
const isNativeEndpoint = isOpenAIResponsesNativeEndpoint(model, env);
|
|
55
56
|
return {
|
|
56
57
|
supportsDeveloperRole: model.compat?.supportsDeveloperRole ?? true,
|
|
58
|
+
supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
|
|
57
59
|
sessionAffinityFormat: model.compat?.sessionAffinityFormat ?? detectSessionAffinityFormat(model),
|
|
58
60
|
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,
|
|
59
61
|
supportsWebSocket: model.compat?.supportsWebSocket ?? isNativeEndpoint,
|
|
@@ -146,11 +148,6 @@ function allowedReference(tool) {
|
|
|
146
148
|
return { type: "mcp", server_label: tool.server_label };
|
|
147
149
|
return { type: tool.type };
|
|
148
150
|
}
|
|
149
|
-
function resolveDeferredToolsMode(compat) {
|
|
150
|
-
if (compat.supportsAdditionalTools)
|
|
151
|
-
return "additional-tools";
|
|
152
|
-
return compat.supportsToolSearch ? "tool-search" : undefined;
|
|
153
|
-
}
|
|
154
151
|
/**
|
|
155
152
|
* senpi#2095: `tools` carries every tool declared this session, so removing a tool never rewrites the
|
|
156
153
|
* cached prefix; the callable subset rides `tool_choice: allowed_tools` instead. Hosted tools a payload
|
|
@@ -158,13 +155,12 @@ function resolveDeferredToolsMode(compat) {
|
|
|
158
155
|
* referenced by name. A declared tool a payload hook removed from `tools` is not referenced (senpi#2234).
|
|
159
156
|
* An empty subset forbids tool calls. An explicit `tool_choice` always wins.
|
|
160
157
|
*/
|
|
161
|
-
function applyAllowedToolsChoice(params, context, compat) {
|
|
162
|
-
const activeToolNames = context.activeToolNames;
|
|
158
|
+
function applyAllowedToolsChoice(params, context, activeToolNames, compat) {
|
|
163
159
|
if (!compat.supportsAllowedTools || activeToolNames === undefined || params.tool_choice !== undefined) {
|
|
164
160
|
return params;
|
|
165
161
|
}
|
|
166
162
|
const active = new Set(activeToolNames);
|
|
167
|
-
const declaredTools = context.
|
|
163
|
+
const declaredTools = getCurrentTools(context.messages);
|
|
168
164
|
if (declaredTools.every((tool) => active.has(tool.name)))
|
|
169
165
|
return params;
|
|
170
166
|
const allowed = [];
|
|
@@ -177,9 +173,10 @@ function applyAllowedToolsChoice(params, context, compat) {
|
|
|
177
173
|
}
|
|
178
174
|
allowed.push(allowedReference(tool));
|
|
179
175
|
}
|
|
180
|
-
|
|
176
|
+
// Tools missing from the request-level `tools` placement are the ones transcript items load in place.
|
|
177
|
+
const requestToolNames = new Set(resolveResponsesToolPlacement(context.messages, resolveResponsesDeferredToolsMode(compat) !== undefined).requestTools.map((tool) => tool.name));
|
|
181
178
|
for (const tool of declaredTools) {
|
|
182
|
-
if (namedInTools.has(tool.name) || !active.has(tool.name) ||
|
|
179
|
+
if (namedInTools.has(tool.name) || !active.has(tool.name) || requestToolNames.has(tool.name))
|
|
183
180
|
continue;
|
|
184
181
|
const [converted] = convertResponsesTools([tool], {
|
|
185
182
|
supportsStrictMode: compat.supportsStrictMode,
|
|
@@ -191,14 +188,19 @@ function applyAllowedToolsChoice(params, context, compat) {
|
|
|
191
188
|
const toolChoice = allowed.length > 0 ? { type: "allowed_tools", mode: "auto", tools: allowed } : "none";
|
|
192
189
|
return { ...params, tool_choice: toolChoice };
|
|
193
190
|
}
|
|
194
|
-
function formatOpenAIResponsesError(error) {
|
|
195
|
-
|
|
191
|
+
function formatOpenAIResponsesError(error, provider) {
|
|
192
|
+
const errorMessage = formatProviderError(normalizeProviderError(error), `${provider === "openai" ? "OpenAI" : provider} API error`);
|
|
193
|
+
// Sign in with ChatGPT shares the subscription's usage limit with other apps.
|
|
194
|
+
return errorMessage.includes("subscription_sharing_usage_limit_exceeded")
|
|
195
|
+
? `${errorMessage}\nCheck your ChatGPT usage: ${CHATGPT_USAGE_URL}`
|
|
196
|
+
: errorMessage;
|
|
196
197
|
}
|
|
197
198
|
/**
|
|
198
199
|
* Generate function for OpenAI Responses API
|
|
199
200
|
*/
|
|
200
201
|
export const stream = (model, context, options) => {
|
|
201
202
|
const stream = new AssistantMessageEventStream();
|
|
203
|
+
const normalizedContext = resolveTranscript(context, getCompat(model).supportsMidConvoSystemMessages);
|
|
202
204
|
// Start async processing
|
|
203
205
|
(async () => {
|
|
204
206
|
const output = {
|
|
@@ -223,15 +225,15 @@ export const stream = (model, context, options) => {
|
|
|
223
225
|
const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
|
|
224
226
|
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
225
227
|
const compat = getCompat(model, options?.env);
|
|
226
|
-
const grammarToolInputProperties = createGrammarToolInputProperties(
|
|
227
|
-
const client = createClient(model,
|
|
228
|
-
let params = buildParams(model,
|
|
228
|
+
const grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(normalizedContext.messages), compat.supportsOpenAIGrammarTools);
|
|
229
|
+
const client = createClient(model, normalizedContext, clientAuth.apiKey, clientAuth.headers, options?.fetch, cacheSessionId, options?.env);
|
|
230
|
+
let params = buildParams(model, normalizedContext, options, compat, grammarToolInputProperties);
|
|
229
231
|
const nextParams = await options?.onPayload?.(params, model);
|
|
230
232
|
if (nextParams !== undefined) {
|
|
231
233
|
params = nextParams;
|
|
232
234
|
}
|
|
233
235
|
params = sanitizeUnsupportedNativeTools(params, compat);
|
|
234
|
-
params = applyAllowedToolsChoice(params, context, compat);
|
|
236
|
+
params = applyAllowedToolsChoice(params, normalizedContext, context.activeToolNames, compat);
|
|
235
237
|
const limitedTools = limitGitHubCopilotTools(model.provider, params.tools, params.tool_choice);
|
|
236
238
|
if (limitedTools.omittedCount > 0) {
|
|
237
239
|
params = { ...params, tools: limitedTools.tools };
|
|
@@ -241,7 +243,7 @@ export const stream = (model, context, options) => {
|
|
|
241
243
|
if (transport !== "sse" && compat.supportsWebSocket) {
|
|
242
244
|
let websocketStarted = false;
|
|
243
245
|
try {
|
|
244
|
-
await processWebSocketStream(resolveOpenAIResponsesWebSocketUrl(model, options?.env), params, buildWebSocketHeaders(model,
|
|
246
|
+
await processWebSocketStream(resolveOpenAIResponsesWebSocketUrl(model, options?.env), params, buildWebSocketHeaders(model, normalizedContext, clientAuth.apiKey, clientAuth.headers, cacheSessionId, options?.env), output, stream, model, () => {
|
|
245
247
|
websocketStarted = true;
|
|
246
248
|
}, cacheSessionId, grammarToolInputProperties, options);
|
|
247
249
|
if (options?.signal?.aborted) {
|
|
@@ -283,6 +285,7 @@ export const stream = (model, context, options) => {
|
|
|
283
285
|
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
284
286
|
stream.push({ type: "start", partial: output });
|
|
285
287
|
await processResponsesStream(openaiStream, output, stream, model, {
|
|
288
|
+
onProviderStreamEvent: options?.onProviderStreamEvent,
|
|
286
289
|
serviceTier: options?.serviceTier,
|
|
287
290
|
grammarToolInputProperties,
|
|
288
291
|
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
|
|
@@ -312,7 +315,7 @@ export const stream = (model, context, options) => {
|
|
|
312
315
|
: undefined;
|
|
313
316
|
if (providerDiagnostic !== undefined)
|
|
314
317
|
output.providerDiagnostic = providerDiagnostic;
|
|
315
|
-
output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error)), model.provider, error);
|
|
318
|
+
output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error, model.provider)), model.provider, error);
|
|
316
319
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
317
320
|
stream.end();
|
|
318
321
|
}
|
|
@@ -344,12 +347,12 @@ function resolveSimpleOptions(model, context, options) {
|
|
|
344
347
|
* prefix matches the reasoning effort, service tier, and cache options that turn sends.
|
|
345
348
|
*/
|
|
346
349
|
export async function warmOpenAIResponsesPromptCache(model, context, options) {
|
|
347
|
-
const prefix = { ...context, messages: [] };
|
|
350
|
+
const prefix = normalizeContext({ ...context, messages: [] });
|
|
348
351
|
const resolved = resolveSimpleOptions(model, prefix, options);
|
|
349
352
|
const clientAuth = resolveOpenAIClientAuth(model.provider, resolved.apiKey, resolved.headers);
|
|
350
353
|
const cacheRetention = resolveCacheRetention(resolved.cacheRetention, resolved.env);
|
|
351
354
|
const compat = getCompat(model, resolved.env);
|
|
352
|
-
const grammarToolInputProperties = createGrammarToolInputProperties(prefix.
|
|
355
|
+
const grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(prefix.messages), compat.supportsOpenAIGrammarTools);
|
|
353
356
|
const client = createClient(model, prefix, clientAuth.apiKey, clientAuth.headers, resolved.fetch, cacheRetention === "none" ? undefined : resolved.sessionId, resolved.env);
|
|
354
357
|
let params = buildParams(model, prefix, resolved, compat, grammarToolInputProperties);
|
|
355
358
|
const nextParams = await resolved.onPayload?.(params, model);
|
|
@@ -419,9 +422,8 @@ function createClient(model, context, apiKey, optionsHeaders, fetch, sessionId,
|
|
|
419
422
|
defaultHeaders: headers,
|
|
420
423
|
});
|
|
421
424
|
}
|
|
422
|
-
function buildParams(model, context, options, compat = getCompat(model, options?.env), grammarToolInputProperties = createGrammarToolInputProperties(context.
|
|
423
|
-
const
|
|
424
|
-
const toolPlacement = splitDeferredTools(context, deferredToolsMode !== undefined);
|
|
425
|
+
function buildParams(model, context, options, compat = getCompat(model, options?.env), grammarToolInputProperties = createGrammarToolInputProperties(getDeclaredTools(context.messages), compat.supportsOpenAIGrammarTools)) {
|
|
426
|
+
const toolPlacement = resolveResponsesToolPlacement(context.messages, resolveResponsesDeferredToolsMode(compat) !== undefined);
|
|
425
427
|
const requestedReasoningEffort = options?.reasoningEffort ?? (options?.reasoningSummary ? "medium" : undefined);
|
|
426
428
|
const thinkingLevelMap = inferOpenAIThinkingLevelMap(model);
|
|
427
429
|
const mappedReasoningEffort = requestedReasoningEffort === undefined ? undefined : thinkingLevelMap?.[requestedReasoningEffort];
|
|
@@ -435,8 +437,9 @@ function buildParams(model, context, options, compat = getCompat(model, options?
|
|
|
435
437
|
// nor a previous prefix unless the system prompt carries an explicit breakpoint.
|
|
436
438
|
systemPromptCacheBreakpoint: compat.supportsExplicitPromptCacheMode && cacheRetention !== "none",
|
|
437
439
|
grammarToolInputProperties,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
+
supportsMidConvoSystemMessages: compat.supportsMidConvoSystemMessages,
|
|
441
|
+
supportsAdditionalTools: compat.supportsAdditionalTools,
|
|
442
|
+
supportsToolSearch: compat.supportsToolSearch,
|
|
440
443
|
toolOptions: {
|
|
441
444
|
supportsStrictMode: compat.supportsStrictMode,
|
|
442
445
|
supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
|
|
@@ -468,8 +471,8 @@ function buildParams(model, context, options, compat = getCompat(model, options?
|
|
|
468
471
|
if (options?.serviceTier !== undefined) {
|
|
469
472
|
params.service_tier = options.serviceTier;
|
|
470
473
|
}
|
|
471
|
-
if (toolPlacement.
|
|
472
|
-
params.tools = convertResponsesTools(toolPlacement.
|
|
474
|
+
if (toolPlacement.requestTools.length > 0) {
|
|
475
|
+
params.tools = convertResponsesTools(toolPlacement.requestTools, {
|
|
473
476
|
supportsStrictMode: compat.supportsStrictMode,
|
|
474
477
|
supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
|
|
475
478
|
});
|
|
@@ -494,10 +497,8 @@ function buildParams(model, context, options, compat = getCompat(model, options?
|
|
|
494
497
|
params.include = ["reasoning.encrypted_content"];
|
|
495
498
|
}
|
|
496
499
|
applyExtraBodyToResponsesParams(params, options?.extraBody);
|
|
497
|
-
// Last so custom keys override the named request fields.
|
|
498
|
-
|
|
499
|
-
Object.assign(params, options.samplingParams);
|
|
500
|
-
}
|
|
500
|
+
// Last so custom keys override the named request fields. Per-request keys override model defaults.
|
|
501
|
+
Object.assign(params, model.samplingParams, options?.samplingParams);
|
|
501
502
|
return params;
|
|
502
503
|
}
|
|
503
504
|
function applyExtraBodyToResponsesParams(params, extraBody) {
|
|
@@ -859,6 +860,10 @@ function buildWebSocketHeaders(model, context, apiKey, optionsHeaders, sessionId
|
|
|
859
860
|
}
|
|
860
861
|
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
861
862
|
switch (serviceTier) {
|
|
863
|
+
case "ultrafast":
|
|
864
|
+
// OpenAI publishes an Ultrafast price for GPT-6 Astra only: 6x Standard on every
|
|
865
|
+
// token class and context tier. Any other model keeps its base rate.
|
|
866
|
+
return model.id === "gpt-6-astra" ? 6 : 1;
|
|
862
867
|
case "flex":
|
|
863
868
|
return 0.5;
|
|
864
869
|
case "priority":
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { ImagesFunction, ImagesOptions } from "../types.ts";
|
|
2
|
-
|
|
2
|
+
/** Image generation over OpenRouter's chat completions endpoint. */
|
|
3
|
+
export declare const generateImages: ImagesFunction<ImagesOptions>;
|
|
3
4
|
//# sourceMappingURL=openrouter-images.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { formatProviderError, normalizeProviderError } from "../utils/error-body
|
|
|
3
3
|
import { headersToRecord, providerHeadersToRecord } from "../utils/headers.js";
|
|
4
4
|
import { retryProviderRequest } from "../utils/provider-retry.js";
|
|
5
5
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
6
|
+
/** Image generation over OpenRouter's chat completions endpoint. */
|
|
6
7
|
export const generateImages = async (model, context, options) => {
|
|
7
8
|
const output = {
|
|
8
9
|
api: model.api,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Radius gateway, but any backend implementing it can be used, e.g. via a
|
|
9
9
|
* models.json custom provider with `"api": "pi-messages"`.
|
|
10
10
|
*/
|
|
11
|
-
import type { AssistantMessage, SimpleStreamOptions, StreamFunction, StreamOptions, ThinkingLevel, ToolCall } from "../types.ts";
|
|
11
|
+
import type { AssistantMessage, JsonObject, SimpleStreamOptions, StreamFunction, StreamOptions, ThinkingLevel, ToolCall } from "../types.ts";
|
|
12
12
|
export interface PiMessagesOptions extends StreamOptions {
|
|
13
13
|
reasoning?: ThinkingLevel;
|
|
14
14
|
toolChoice?: "auto" | "none" | "required" | {
|
|
@@ -90,8 +90,8 @@ export type PiMessagesEvent = {
|
|
|
90
90
|
};
|
|
91
91
|
export declare class PiMessagesResponseError extends Error {
|
|
92
92
|
code?: string;
|
|
93
|
-
readonly diagnosticDetails:
|
|
94
|
-
constructor(message: string, code: string | undefined, diagnosticDetails:
|
|
93
|
+
readonly diagnosticDetails: JsonObject;
|
|
94
|
+
constructor(message: string, code: string | undefined, diagnosticDetails: JsonObject);
|
|
95
95
|
}
|
|
96
96
|
export declare const stream: StreamFunction<"pi-messages", PiMessagesOptions>;
|
|
97
97
|
export declare const streamSimple: StreamFunction<"pi-messages", SimpleStreamOptions>;
|
package/dist/api/pi-messages.js
CHANGED
|
@@ -52,8 +52,8 @@ function createPiMessagesResponseError(model, url, response, body) {
|
|
|
52
52
|
url: url.toString(),
|
|
53
53
|
status: response.status,
|
|
54
54
|
statusText: response.statusText,
|
|
55
|
-
error: errorBody
|
|
56
|
-
|
|
55
|
+
...(errorBody?.error === undefined ? {} : { error: errorBody.error }),
|
|
56
|
+
...(errorBody ? {} : { body: truncateDiagnosticString(body) }),
|
|
57
57
|
timestampMs: Date.now(),
|
|
58
58
|
});
|
|
59
59
|
}
|
|
@@ -301,6 +301,7 @@ export const stream = (model, context, options) => {
|
|
|
301
301
|
throw new Error(`${model.provider} response has no body`);
|
|
302
302
|
}
|
|
303
303
|
for await (const piEvent of readPiMessagesEvents(response.body)) {
|
|
304
|
+
await options?.onProviderStreamEvent?.(piEvent, model);
|
|
304
305
|
const event = convertEvent(piEvent);
|
|
305
306
|
eventStream.push(event);
|
|
306
307
|
if (event.type === "done" || event.type === "error") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Api,
|
|
1
|
+
import type { Api, Model, SimpleStreamOptions, StreamOptions, ThinkingBudgets, ThinkingLevel, TranscriptContext } from "../types.ts";
|
|
2
2
|
export { CONTEXT_GUARD_MIN_WINDOW, CONTEXT_SAFETY_TOKENS, ContextWindowExhaustedError, clampMaxTokensToContext, MIN_ANSWER_TOKENS, } from "./context-room.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Merge user-supplied extraBody fields into a provider request payload, skipping
|
|
@@ -17,7 +17,7 @@ export declare const GOOGLE_RESERVED_BODY_KEYS: ReadonlySet<string>;
|
|
|
17
17
|
export declare const ANTHROPIC_RESERVED_BODY_KEYS: ReadonlySet<string>;
|
|
18
18
|
export declare const MISTRAL_RESERVED_BODY_KEYS: ReadonlySet<string>;
|
|
19
19
|
export declare const BEDROCK_RESERVED_BODY_KEYS: ReadonlySet<string>;
|
|
20
|
-
export declare function buildBaseOptions(model: Model<Api>, context:
|
|
20
|
+
export declare function buildBaseOptions(model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions, apiKey?: string): StreamOptions;
|
|
21
21
|
export declare const DEFAULT_THINKING_BUDGETS: ThinkingBudgets;
|
|
22
22
|
export declare function clampReasoning(effort: ThinkingLevel | undefined): Exclude<ThinkingLevel, "xhigh" | "max"> | undefined;
|
|
23
23
|
/**
|
|
@@ -122,6 +122,7 @@ export function buildBaseOptions(model, context, options, apiKey) {
|
|
|
122
122
|
extraBody: options?.extraBody,
|
|
123
123
|
onPayload: options?.onPayload,
|
|
124
124
|
onResponse: options?.onResponse,
|
|
125
|
+
onProviderStreamEvent: options?.onProviderStreamEvent,
|
|
125
126
|
timeoutMs: options?.timeoutMs,
|
|
126
127
|
websocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,
|
|
127
128
|
maxRetries: options?.maxRetries,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ClassifierApi, ClassifierContext, ClassifierModel, ClassifierOptions, ClassifierResult } from "../types.ts";
|
|
2
|
+
/** TypeSafe System One request body without the transport-specific envelope. */
|
|
3
|
+
export interface SystemOneWireRequest {
|
|
4
|
+
state: ClassifierContext["state"];
|
|
5
|
+
questions: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
/** Differences between services that serve System One models. */
|
|
8
|
+
export interface SystemOneTransport {
|
|
9
|
+
/** Classifier API implemented by this transport. */
|
|
10
|
+
api: ClassifierApi;
|
|
11
|
+
/** Service name used in error messages. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Absolute request URL. */
|
|
14
|
+
url(model: ClassifierModel<ClassifierApi>): URL;
|
|
15
|
+
/** Wraps the System One request in the service's request envelope. */
|
|
16
|
+
payload(model: ClassifierModel<ClassifierApi>, request: SystemOneWireRequest): unknown;
|
|
17
|
+
/** Extracts the System One output (`{ answers, usage }`) from the service's response envelope. */
|
|
18
|
+
output(body: unknown): Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
21
|
+
/** Runs one System One classification over the given transport. */
|
|
22
|
+
export declare function classifySystemOne(transport: SystemOneTransport, model: ClassifierModel<ClassifierApi>, context: ClassifierContext, options: ClassifierOptions | undefined): Promise<ClassifierResult>;
|
|
23
|
+
//# sourceMappingURL=system-one-shared.d.ts.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { calculateCost } from "../models.js";
|
|
2
|
+
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
3
|
+
import { headersToRecord, providerHeadersToRecord } from "../utils/headers.js";
|
|
4
|
+
import { retryProviderRequest } from "../utils/provider-retry.js";
|
|
5
|
+
function httpError(label, response, body) {
|
|
6
|
+
const error = new Error(`${label} returned ${response.status}`);
|
|
7
|
+
error.status = response.status;
|
|
8
|
+
error.headers = response.headers;
|
|
9
|
+
error.body = body;
|
|
10
|
+
return error;
|
|
11
|
+
}
|
|
12
|
+
function timeoutError(timeoutMs) {
|
|
13
|
+
const error = new Error(`Request timed out after ${timeoutMs}ms`);
|
|
14
|
+
error.name = "TimeoutError";
|
|
15
|
+
error.status = undefined;
|
|
16
|
+
error.headers = undefined;
|
|
17
|
+
error.body = "";
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
export function isRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function requiredNumber(label, value, field) {
|
|
24
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
25
|
+
throw new Error(`${label} returned an invalid ${field}`);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function probabilities(label, value, id) {
|
|
30
|
+
if (!isRecord(value))
|
|
31
|
+
throw new Error(`${label} returned invalid probabilities for ${id}`);
|
|
32
|
+
return Object.fromEntries(Object.entries(value).map(([key, probability]) => [
|
|
33
|
+
key,
|
|
34
|
+
requiredNumber(label, probability, `probability for ${id}.${key}`),
|
|
35
|
+
]));
|
|
36
|
+
}
|
|
37
|
+
function parseAnswers(label, value, context) {
|
|
38
|
+
if (!isRecord(value))
|
|
39
|
+
throw new Error(`${label} returned an unexpected response`);
|
|
40
|
+
const answers = [];
|
|
41
|
+
for (const [id, question] of Object.entries(context.questions)) {
|
|
42
|
+
const answer = value[id];
|
|
43
|
+
if (!isRecord(answer))
|
|
44
|
+
throw new Error(`${label} did not return an answer for ${id}`);
|
|
45
|
+
if (question.type === "choice") {
|
|
46
|
+
if (answer.type !== "choice" || typeof answer.choice !== "string") {
|
|
47
|
+
throw new Error(`${label} did not return a choice answer for ${id}`);
|
|
48
|
+
}
|
|
49
|
+
answers.push([
|
|
50
|
+
id,
|
|
51
|
+
{
|
|
52
|
+
type: "choice",
|
|
53
|
+
choice: answer.choice,
|
|
54
|
+
probabilities: probabilities(label, answer.probabilities, id),
|
|
55
|
+
confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`),
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
else if (question.type === "score") {
|
|
60
|
+
if (answer.type !== "score")
|
|
61
|
+
throw new Error(`${label} did not return a score answer for ${id}`);
|
|
62
|
+
answers.push([
|
|
63
|
+
id,
|
|
64
|
+
{
|
|
65
|
+
type: "score",
|
|
66
|
+
score: requiredNumber(label, answer.score, `score for ${id}`),
|
|
67
|
+
confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`),
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (answer.type !== "noul")
|
|
73
|
+
throw new Error(`${label} did not return a bool answer for ${id}`);
|
|
74
|
+
answers.push([
|
|
75
|
+
id,
|
|
76
|
+
{
|
|
77
|
+
type: "bool",
|
|
78
|
+
probability: requiredNumber(label, answer.noul, `probability for ${id}`),
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Object.fromEntries(answers);
|
|
84
|
+
}
|
|
85
|
+
function tokenCount(value) {
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Usage from System One's `{ input_tokens, output_tokens }`, priced from the model catalog like chat
|
|
90
|
+
* usage. A missing or malformed usage object leaves the result without usage instead of failing it.
|
|
91
|
+
*/
|
|
92
|
+
function parseUsage(value, model) {
|
|
93
|
+
if (!isRecord(value) || (value.input_tokens === undefined && value.output_tokens === undefined))
|
|
94
|
+
return undefined;
|
|
95
|
+
const input = tokenCount(value.input_tokens);
|
|
96
|
+
const output = tokenCount(value.output_tokens);
|
|
97
|
+
const usage = {
|
|
98
|
+
input,
|
|
99
|
+
output,
|
|
100
|
+
cacheRead: 0,
|
|
101
|
+
cacheWrite: 0,
|
|
102
|
+
totalTokens: input + output,
|
|
103
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
104
|
+
};
|
|
105
|
+
calculateCost(model, usage);
|
|
106
|
+
return usage;
|
|
107
|
+
}
|
|
108
|
+
/** Maps public `bool` questions to TypeSafe's wire-level `noul` type. */
|
|
109
|
+
function wireRequest(context) {
|
|
110
|
+
return {
|
|
111
|
+
state: context.state,
|
|
112
|
+
questions: Object.fromEntries(Object.entries(context.questions).map(([id, question]) => [
|
|
113
|
+
id,
|
|
114
|
+
question.type === "bool" ? { ...question, type: "noul" } : question,
|
|
115
|
+
])),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function requestHeaders(model, apiKey, optionsHeaders) {
|
|
119
|
+
return (providerHeadersToRecord({ authorization: `Bearer ${apiKey}`, "content-type": "application/json" }, model.headers, optionsHeaders) ?? {});
|
|
120
|
+
}
|
|
121
|
+
/** Runs one System One classification over the given transport. */
|
|
122
|
+
export async function classifySystemOne(transport, model, context, options) {
|
|
123
|
+
const output = {
|
|
124
|
+
api: model.api,
|
|
125
|
+
provider: model.provider,
|
|
126
|
+
model: model.id,
|
|
127
|
+
answers: {},
|
|
128
|
+
stopReason: "stop",
|
|
129
|
+
timestamp: Date.now(),
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
if (model.api !== transport.api)
|
|
133
|
+
throw new Error(`Unsupported classifier API: ${model.api}`);
|
|
134
|
+
if (!options?.apiKey)
|
|
135
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
136
|
+
const apiKey = options.apiKey;
|
|
137
|
+
let payload = transport.payload(model, wireRequest(context));
|
|
138
|
+
const transformed = await options.onPayload?.(payload, model);
|
|
139
|
+
if (transformed !== undefined)
|
|
140
|
+
payload = transformed;
|
|
141
|
+
const requestFetch = options.fetch ?? globalThis.fetch;
|
|
142
|
+
const { response, body } = await retryProviderRequest(async () => {
|
|
143
|
+
const timeoutSignal = options.timeoutMs !== undefined ? AbortSignal.timeout(options.timeoutMs) : undefined;
|
|
144
|
+
const signal = options.signal && timeoutSignal
|
|
145
|
+
? AbortSignal.any([options.signal, timeoutSignal])
|
|
146
|
+
: (options.signal ?? timeoutSignal);
|
|
147
|
+
try {
|
|
148
|
+
const next = await requestFetch(transport.url(model), {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: requestHeaders(model, apiKey, options.headers),
|
|
151
|
+
body: JSON.stringify(payload),
|
|
152
|
+
signal,
|
|
153
|
+
});
|
|
154
|
+
if (!next.ok)
|
|
155
|
+
throw httpError(transport.label, next, await next.text());
|
|
156
|
+
return { response: next, body: (await next.json()) };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (timeoutSignal?.aborted && !options.signal?.aborted)
|
|
160
|
+
throw timeoutError(options.timeoutMs);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}, {
|
|
164
|
+
maxRetries: options.maxRetries ?? 2,
|
|
165
|
+
maxRetryDelayMs: options.maxRetryDelayMs,
|
|
166
|
+
signal: options.signal,
|
|
167
|
+
});
|
|
168
|
+
await options.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
169
|
+
const result = transport.output(body);
|
|
170
|
+
// Set before parsing answers: a request with malformed answers was still billed.
|
|
171
|
+
const usage = parseUsage(result.usage, model);
|
|
172
|
+
if (usage)
|
|
173
|
+
output.usage = usage;
|
|
174
|
+
output.answers = parseAnswers(transport.label, result.answers, context);
|
|
175
|
+
return output;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
179
|
+
output.errorMessage = formatProviderError(normalizeProviderError(error), `${transport.label} error`);
|
|
180
|
+
return output;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=system-one-shared.js.map
|
|
@@ -88,8 +88,8 @@ export function transformMessages(messages, model, normalizeToolCallId, options
|
|
|
88
88
|
const normalizeSameModelToolCallIds = options.normalizeSameModelToolCallIds ?? false;
|
|
89
89
|
// First pass: transform messages (unsupported image downgrade, thinking blocks, tool call ID normalization)
|
|
90
90
|
const transformed = imageAwareMessages.map((msg) => {
|
|
91
|
-
//
|
|
92
|
-
if (msg.role === "user") {
|
|
91
|
+
// System and user messages pass through unchanged
|
|
92
|
+
if (msg.role === "system" || msg.role === "user") {
|
|
93
93
|
return msg;
|
|
94
94
|
}
|
|
95
95
|
// Handle toolResult messages - normalize toolCallId if we have a mapping
|
|
@@ -168,6 +168,9 @@ export function transformMessages(messages, model, normalizeToolCallId, options
|
|
|
168
168
|
// result after its declaring assistant. Results are emitted adjacent to their
|
|
169
169
|
// calls because Anthropic requires that ordering. A reused ID starts a new
|
|
170
170
|
// pairing window, so a later result cannot repair an earlier call.
|
|
171
|
+
// System messages are transparent to this accounting: they keep their source
|
|
172
|
+
// position, and because matched results are pulled forward, one that lands
|
|
173
|
+
// between a call and its result is emitted after that result.
|
|
171
174
|
const result = [];
|
|
172
175
|
const toolResultsById = new Map();
|
|
173
176
|
const nextToolCallIndexById = new Map();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ClassifierFunction, ClassifierOptions } from "../types.ts";
|
|
2
|
+
/** TypeSafe System One classification with public `bool` values mapped to wire-level `noul`. */
|
|
3
|
+
export declare const classify: ClassifierFunction<ClassifierOptions>;
|
|
4
|
+
//# sourceMappingURL=typesafe-system-one.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { classifySystemOne, isRecord } from "./system-one-shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* TypeSafe's native System One protocol. OpenRouter serves the same protocol,
|
|
4
|
+
* so both providers use this API with different base URLs.
|
|
5
|
+
*/
|
|
6
|
+
const transport = {
|
|
7
|
+
api: "typesafe-system-one",
|
|
8
|
+
label: "System One API",
|
|
9
|
+
url: (model) => new URL("systemone", `${model.baseUrl.replace(/\/+$/u, "")}/`),
|
|
10
|
+
payload: (model, request) => ({ model: model.id, ...request }),
|
|
11
|
+
output: (body) => {
|
|
12
|
+
if (!isRecord(body))
|
|
13
|
+
throw new Error("System One API returned an unexpected response");
|
|
14
|
+
return body;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
/** TypeSafe System One classification with public `bool` values mapped to wire-level `noul`. */
|
|
18
|
+
export const classify = (model, context, options) => classifySystemOne(transport, model, context, options);
|
|
19
|
+
//# sourceMappingURL=typesafe-system-one.js.map
|
package/dist/api-registry.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Api, AssistantMessageEventStream,
|
|
2
|
-
export type ApiStreamFunction = (model: Model<Api>, context:
|
|
3
|
-
export type ApiStreamSimpleFunction = (model: Model<Api>, context:
|
|
1
|
+
import type { Api, AssistantMessageEventStream, Model, SimpleStreamOptions, StreamFunction, StreamOptions, TranscriptContext } from "./types.ts";
|
|
2
|
+
export type ApiStreamFunction = (model: Model<Api>, context: TranscriptContext, options?: StreamOptions) => AssistantMessageEventStream;
|
|
3
|
+
export type ApiStreamSimpleFunction = (model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) => AssistantMessageEventStream;
|
|
4
4
|
export interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {
|
|
5
5
|
api: TApi;
|
|
6
6
|
stream: StreamFunction<TApi, TOptions>;
|
package/dist/auth/helpers.js
CHANGED
|
@@ -46,7 +46,7 @@ export function lazyOAuth(input) {
|
|
|
46
46
|
isSubscription: input.isSubscription,
|
|
47
47
|
loginLabel: input.loginLabel,
|
|
48
48
|
...(input.rejectedTokenStatuses === undefined ? {} : { rejectedTokenStatuses: input.rejectedTokenStatuses }),
|
|
49
|
-
login: async (interaction) => (await loaded()).login(interaction),
|
|
49
|
+
login: async (interaction, options) => (await loaded()).login(interaction, options),
|
|
50
50
|
refresh: async (credential, signal) => (await loaded()).refresh(credential, signal),
|
|
51
51
|
toAuth: async (credential) => (await loaded()).toAuth(credential),
|
|
52
52
|
};
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* NOTE: Node-only (http.createServer); reached through the lazy OAuth loader.
|
|
12
12
|
*/
|
|
13
|
+
import { oauthErrorHtml, oauthSuccessHtml } from "../../utils/oauth-page.js";
|
|
13
14
|
import { formatErrorDetails } from "./error-details.js";
|
|
14
|
-
import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
|
|
15
15
|
export const PREFERRED_CALLBACK_PORT = 53692;
|
|
16
16
|
export const CALLBACK_PATH = "/callback";
|
|
17
17
|
const BIND_FAILURE_CODES = new Set(["EACCES", "EADDRINUSE", "EPERM"]);
|