@prometheus-ai/ai 0.5.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/CHANGELOG.md +7 -0
- package/README.md +1184 -0
- package/dist/types/api-registry.d.ts +30 -0
- package/dist/types/auth-broker/client.d.ts +66 -0
- package/dist/types/auth-broker/index.d.ts +6 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +101 -0
- package/dist/types/auth-broker/server.d.ts +32 -0
- package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
- package/dist/types/auth-broker/types.d.ts +107 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
- package/dist/types/auth-gateway/http.d.ts +39 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +36 -0
- package/dist/types/auth-gateway/types.d.ts +117 -0
- package/dist/types/auth-storage.d.ts +762 -0
- package/dist/types/index.d.ts +49 -0
- package/dist/types/model-cache.d.ts +17 -0
- package/dist/types/model-manager.d.ts +64 -0
- package/dist/types/model-thinking.d.ts +100 -0
- package/dist/types/models.d.ts +12 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/provider-models/bundled-references.d.ts +4 -0
- package/dist/types/provider-models/descriptors.d.ts +50 -0
- package/dist/types/provider-models/google.d.ts +24 -0
- package/dist/types/provider-models/index.d.ts +5 -0
- package/dist/types/provider-models/ollama.d.ts +7 -0
- package/dist/types/provider-models/openai-compat.d.ts +323 -0
- package/dist/types/provider-models/special.d.ts +16 -0
- package/dist/types/providers/amazon-bedrock.d.ts +38 -0
- package/dist/types/providers/anthropic-client.d.ts +99 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +465 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic-wire.d.ts +262 -0
- package/dist/types/providers/anthropic.d.ts +206 -0
- package/dist/types/providers/aws-credentials.d.ts +43 -0
- package/dist/types/providers/aws-eventstream.d.ts +38 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +15 -0
- package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
- package/dist/types/providers/cursor.d.ts +43 -0
- package/dist/types/providers/error-message.d.ts +27 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -0
- package/dist/types/providers/gitlab-duo.d.ts +27 -0
- package/dist/types/providers/google-auth.d.ts +24 -0
- package/dist/types/providers/google-gemini-cli.d.ts +81 -0
- package/dist/types/providers/google-gemini-headers.d.ts +18 -0
- package/dist/types/providers/google-shared.d.ts +171 -0
- package/dist/types/providers/google-types.d.ts +138 -0
- package/dist/types/providers/google-vertex.d.ts +7 -0
- package/dist/types/providers/google.d.ts +4 -0
- package/dist/types/providers/grammar.d.ts +1 -0
- package/dist/types/providers/kimi.d.ts +27 -0
- package/dist/types/providers/mock.d.ts +173 -0
- package/dist/types/providers/ollama.d.ts +6 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +817 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-codex/constants.d.ts +26 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
- package/dist/types/providers/openai-codex-responses.d.ts +67 -0
- package/dist/types/providers/openai-completions-compat.d.ts +27 -0
- package/dist/types/providers/openai-completions.d.ts +54 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-shared.d.ts +105 -0
- package/dist/types/providers/openai-responses.d.ts +66 -0
- package/dist/types/providers/prometheus-native-client.d.ts +13 -0
- package/dist/types/providers/prometheus-native-server.d.ts +68 -0
- package/dist/types/providers/register-builtins.d.ts +31 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +12 -0
- package/dist/types/providers/vision-guard.d.ts +20 -0
- package/dist/types/providers/xai-responses.d.ts +23 -0
- package/dist/types/rate-limit-utils.d.ts +19 -0
- package/dist/types/stream.d.ts +28 -0
- package/dist/types/types.d.ts +819 -0
- package/dist/types/usage/claude.d.ts +4 -0
- package/dist/types/usage/gemini.d.ts +2 -0
- package/dist/types/usage/github-copilot.d.ts +7 -0
- package/dist/types/usage/google-antigravity.d.ts +2 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +2 -0
- package/dist/types/usage/openai-codex.d.ts +3 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/zai.d.ts +2 -0
- package/dist/types/usage.d.ts +260 -0
- package/dist/types/utils/abort.d.ts +19 -0
- package/dist/types/utils/abortable-iterator.d.ts +4 -0
- package/dist/types/utils/anthropic-auth.d.ts +35 -0
- package/dist/types/utils/discovery/antigravity.d.ts +61 -0
- package/dist/types/utils/discovery/codex.d.ts +38 -0
- package/dist/types/utils/discovery/cursor.d.ts +23 -0
- package/dist/types/utils/discovery/gemini.d.ts +25 -0
- package/dist/types/utils/discovery/index.d.ts +4 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
- package/dist/types/utils/event-stream.d.ts +28 -0
- package/dist/types/utils/fireworks-model-id.d.ts +10 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/http-inspector.d.ts +31 -0
- package/dist/types/utils/idle-iterator.d.ts +78 -0
- package/dist/types/utils/json-parse.d.ts +37 -0
- package/dist/types/utils/oauth/__tests__/xai-oauth.test.d.ts +1 -0
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
- package/dist/types/utils/oauth/anthropic.d.ts +22 -0
- package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
- package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
- package/dist/types/utils/oauth/callback-server.d.ts +57 -0
- package/dist/types/utils/oauth/cerebras.d.ts +1 -0
- package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/cursor.d.ts +15 -0
- package/dist/types/utils/oauth/deepseek.d.ts +10 -0
- package/dist/types/utils/oauth/firepass.d.ts +1 -0
- package/dist/types/utils/oauth/fireworks.d.ts +1 -0
- package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
- package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
- package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
- package/dist/types/utils/oauth/huggingface.d.ts +19 -0
- package/dist/types/utils/oauth/index.d.ts +38 -0
- package/dist/types/utils/oauth/kagi.d.ts +17 -0
- package/dist/types/utils/oauth/kilo.d.ts +5 -0
- package/dist/types/utils/oauth/kimi.d.ts +21 -0
- package/dist/types/utils/oauth/litellm.d.ts +18 -0
- package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
- package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
- package/dist/types/utils/oauth/moonshot.d.ts +1 -0
- package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
- package/dist/types/utils/oauth/nvidia.d.ts +18 -0
- package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
- package/dist/types/utils/oauth/ollama.d.ts +18 -0
- package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
- package/dist/types/utils/oauth/opencode.d.ts +18 -0
- package/dist/types/utils/oauth/openrouter.d.ts +1 -0
- package/dist/types/utils/oauth/parallel.d.ts +17 -0
- package/dist/types/utils/oauth/perplexity.d.ts +9 -0
- package/dist/types/utils/oauth/pkce.d.ts +8 -0
- package/dist/types/utils/oauth/qianfan.d.ts +17 -0
- package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
- package/dist/types/utils/oauth/synthetic.d.ts +1 -0
- package/dist/types/utils/oauth/tavily.d.ts +17 -0
- package/dist/types/utils/oauth/together.d.ts +1 -0
- package/dist/types/utils/oauth/types.d.ts +44 -0
- package/dist/types/utils/oauth/venice.d.ts +18 -0
- package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/vllm.d.ts +16 -0
- package/dist/types/utils/oauth/wafer.d.ts +2 -0
- package/dist/types/utils/oauth/xai-oauth.d.ts +60 -0
- package/dist/types/utils/oauth/xiaomi.d.ts +25 -0
- package/dist/types/utils/oauth/zai.d.ts +18 -0
- package/dist/types/utils/oauth/zenmux.d.ts +1 -0
- package/dist/types/utils/oauth/zhipu.d.ts +18 -0
- package/dist/types/utils/overflow.d.ts +54 -0
- package/dist/types/utils/parse-bind.d.ts +23 -0
- package/dist/types/utils/provider-response.d.ts +3 -0
- package/dist/types/utils/request-debug.d.ts +29 -0
- package/dist/types/utils/retry-after.d.ts +3 -0
- package/dist/types/utils/retry.d.ts +26 -0
- package/dist/types/utils/schema/adapt.d.ts +24 -0
- package/dist/types/utils/schema/compatibility.d.ts +30 -0
- package/dist/types/utils/schema/dereference.d.ts +11 -0
- package/dist/types/utils/schema/draft.d.ts +10 -0
- package/dist/types/utils/schema/equality.d.ts +4 -0
- package/dist/types/utils/schema/fields.d.ts +49 -0
- package/dist/types/utils/schema/index.d.ts +13 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +93 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +25 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/wire.d.ts +53 -0
- package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
- package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
- package/dist/types/utils/sse-debug.d.ts +10 -0
- package/dist/types/utils/stream-markup-healing.d.ts +80 -0
- package/dist/types/utils/tool-choice.d.ts +50 -0
- package/dist/types/utils/validation.d.ts +17 -0
- package/dist/types/utils.d.ts +28 -0
- package/package.json +142 -0
- package/src/api-registry.ts +96 -0
- package/src/auth-broker/client.ts +358 -0
- package/src/auth-broker/index.ts +6 -0
- package/src/auth-broker/refresher.ts +117 -0
- package/src/auth-broker/remote-store.ts +637 -0
- package/src/auth-broker/server.ts +644 -0
- package/src/auth-broker/snapshot-cache.ts +174 -0
- package/src/auth-broker/types.ts +130 -0
- package/src/auth-broker/wire-schemas.ts +200 -0
- package/src/auth-gateway/http.ts +194 -0
- package/src/auth-gateway/index.ts +3 -0
- package/src/auth-gateway/server.ts +822 -0
- package/src/auth-gateway/types.ts +143 -0
- package/src/auth-storage.ts +4608 -0
- package/src/index.ts +54 -0
- package/src/model-cache.ts +129 -0
- package/src/model-manager.ts +469 -0
- package/src/model-thinking.ts +756 -0
- package/src/models.json +60287 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +56 -0
- package/src/prompts/turn-aborted-guidance.md +4 -0
- package/src/provider-details.ts +90 -0
- package/src/provider-models/bundled-references.ts +38 -0
- package/src/provider-models/descriptors.ts +364 -0
- package/src/provider-models/google.ts +88 -0
- package/src/provider-models/index.ts +5 -0
- package/src/provider-models/ollama.ts +153 -0
- package/src/provider-models/openai-compat.ts +2904 -0
- package/src/provider-models/special.ts +67 -0
- package/src/providers/amazon-bedrock.ts +873 -0
- package/src/providers/anthropic-client.ts +318 -0
- package/src/providers/anthropic-messages-server-schema.ts +243 -0
- package/src/providers/anthropic-messages-server.ts +681 -0
- package/src/providers/anthropic-wire.ts +268 -0
- package/src/providers/anthropic.ts +3106 -0
- package/src/providers/aws-credentials.ts +501 -0
- package/src/providers/aws-eventstream.ts +185 -0
- package/src/providers/aws-sigv4.ts +218 -0
- package/src/providers/azure-openai-responses.ts +361 -0
- package/src/providers/cursor/gen/agent_pb.ts +15274 -0
- package/src/providers/cursor/proto/agent.proto +3526 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor.ts +2621 -0
- package/src/providers/error-message.ts +21 -0
- package/src/providers/github-copilot-headers.ts +140 -0
- package/src/providers/gitlab-duo.ts +372 -0
- package/src/providers/google-auth.ts +252 -0
- package/src/providers/google-gemini-cli.ts +809 -0
- package/src/providers/google-gemini-headers.ts +41 -0
- package/src/providers/google-shared.ts +917 -0
- package/src/providers/google-types.ts +167 -0
- package/src/providers/google-vertex.ts +91 -0
- package/src/providers/google.ts +41 -0
- package/src/providers/grammar.ts +70 -0
- package/src/providers/kimi.ts +52 -0
- package/src/providers/mock.ts +496 -0
- package/src/providers/ollama.ts +644 -0
- package/src/providers/openai-anthropic-shim.ts +138 -0
- package/src/providers/openai-chat-server-schema.ts +252 -0
- package/src/providers/openai-chat-server.ts +647 -0
- package/src/providers/openai-codex/constants.ts +43 -0
- package/src/providers/openai-codex/request-transformer.ts +161 -0
- package/src/providers/openai-codex/response-handler.ts +81 -0
- package/src/providers/openai-codex-responses.ts +3027 -0
- package/src/providers/openai-completions-compat.ts +320 -0
- package/src/providers/openai-completions.ts +2002 -0
- package/src/providers/openai-responses-server-schema.ts +290 -0
- package/src/providers/openai-responses-server.ts +1183 -0
- package/src/providers/openai-responses-shared.ts +956 -0
- package/src/providers/openai-responses.ts +679 -0
- package/src/providers/prometheus-native-client.ts +228 -0
- package/src/providers/prometheus-native-server.ts +212 -0
- package/src/providers/register-builtins.ts +457 -0
- package/src/providers/synthetic.ts +50 -0
- package/src/providers/transform-messages.ts +382 -0
- package/src/providers/vision-guard.ts +52 -0
- package/src/providers/xai-responses.ts +82 -0
- package/src/rate-limit-utils.ts +91 -0
- package/src/stream.ts +1068 -0
- package/src/types.ts +965 -0
- package/src/usage/claude.ts +482 -0
- package/src/usage/gemini.ts +250 -0
- package/src/usage/github-copilot.ts +421 -0
- package/src/usage/google-antigravity.ts +201 -0
- package/src/usage/kimi.ts +271 -0
- package/src/usage/minimax-code.ts +31 -0
- package/src/usage/openai-codex.ts +503 -0
- package/src/usage/shared.ts +10 -0
- package/src/usage/zai.ts +247 -0
- package/src/usage.ts +185 -0
- package/src/utils/abort.ts +51 -0
- package/src/utils/abortable-iterator.ts +69 -0
- package/src/utils/anthropic-auth.ts +93 -0
- package/src/utils/discovery/antigravity.ts +261 -0
- package/src/utils/discovery/codex.ts +371 -0
- package/src/utils/discovery/cursor.ts +306 -0
- package/src/utils/discovery/gemini.ts +248 -0
- package/src/utils/discovery/index.ts +4 -0
- package/src/utils/discovery/openai-compatible.ts +224 -0
- package/src/utils/event-stream.ts +142 -0
- package/src/utils/fireworks-model-id.ts +30 -0
- package/src/utils/foundry.ts +8 -0
- package/src/utils/http-inspector.ts +176 -0
- package/src/utils/idle-iterator.ts +273 -0
- package/src/utils/json-parse.ts +182 -0
- package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
- package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
- package/src/utils/oauth/anthropic.ts +273 -0
- package/src/utils/oauth/api-key-login.ts +87 -0
- package/src/utils/oauth/api-key-validation.ts +92 -0
- package/src/utils/oauth/callback-server.ts +276 -0
- package/src/utils/oauth/cerebras.ts +16 -0
- package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
- package/src/utils/oauth/cursor.ts +157 -0
- package/src/utils/oauth/deepseek.ts +53 -0
- package/src/utils/oauth/firepass.ts +24 -0
- package/src/utils/oauth/fireworks.ts +15 -0
- package/src/utils/oauth/github-copilot.ts +362 -0
- package/src/utils/oauth/gitlab-duo.ts +123 -0
- package/src/utils/oauth/google-antigravity.ts +200 -0
- package/src/utils/oauth/google-gemini-cli.ts +256 -0
- package/src/utils/oauth/google-oauth-shared.ts +110 -0
- package/src/utils/oauth/huggingface.ts +62 -0
- package/src/utils/oauth/index.ts +502 -0
- package/src/utils/oauth/kagi.ts +47 -0
- package/src/utils/oauth/kilo.ts +87 -0
- package/src/utils/oauth/kimi.ts +254 -0
- package/src/utils/oauth/litellm.ts +47 -0
- package/src/utils/oauth/lm-studio.ts +38 -0
- package/src/utils/oauth/minimax-code.ts +80 -0
- package/src/utils/oauth/moonshot.ts +23 -0
- package/src/utils/oauth/nanogpt.ts +15 -0
- package/src/utils/oauth/nvidia.ts +70 -0
- package/src/utils/oauth/oauth.html +199 -0
- package/src/utils/oauth/ollama-cloud.ts +28 -0
- package/src/utils/oauth/ollama.ts +47 -0
- package/src/utils/oauth/openai-codex.ts +299 -0
- package/src/utils/oauth/opencode.ts +49 -0
- package/src/utils/oauth/openrouter.ts +20 -0
- package/src/utils/oauth/parallel.ts +46 -0
- package/src/utils/oauth/perplexity.ts +206 -0
- package/src/utils/oauth/pkce.ts +18 -0
- package/src/utils/oauth/qianfan.ts +58 -0
- package/src/utils/oauth/qwen-portal.ts +60 -0
- package/src/utils/oauth/synthetic.ts +15 -0
- package/src/utils/oauth/tavily.ts +46 -0
- package/src/utils/oauth/together.ts +16 -0
- package/src/utils/oauth/types.ts +102 -0
- package/src/utils/oauth/venice.ts +59 -0
- package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
- package/src/utils/oauth/vllm.ts +40 -0
- package/src/utils/oauth/wafer.ts +50 -0
- package/src/utils/oauth/xai-oauth.ts +342 -0
- package/src/utils/oauth/xiaomi.ts +194 -0
- package/src/utils/oauth/zai.ts +60 -0
- package/src/utils/oauth/zenmux.ts +15 -0
- package/src/utils/oauth/zhipu.ts +60 -0
- package/src/utils/overflow.ts +137 -0
- package/src/utils/parse-bind.ts +54 -0
- package/src/utils/provider-response.ts +30 -0
- package/src/utils/request-debug.ts +336 -0
- package/src/utils/retry-after.ts +110 -0
- package/src/utils/retry.ts +54 -0
- package/src/utils/schema/CONSTRAINTS.md +164 -0
- package/src/utils/schema/adapt.ts +36 -0
- package/src/utils/schema/compatibility.ts +435 -0
- package/src/utils/schema/dereference.ts +98 -0
- package/src/utils/schema/draft.ts +341 -0
- package/src/utils/schema/equality.ts +97 -0
- package/src/utils/schema/fields.ts +191 -0
- package/src/utils/schema/index.ts +13 -0
- package/src/utils/schema/json-schema-validator.ts +577 -0
- package/src/utils/schema/meta-validator.ts +167 -0
- package/src/utils/schema/normalize.ts +1588 -0
- package/src/utils/schema/spill.ts +43 -0
- package/src/utils/schema/stamps.ts +97 -0
- package/src/utils/schema/types.ts +10 -0
- package/src/utils/schema/wire.ts +293 -0
- package/src/utils/schema/zod-decontaminate.ts +331 -0
- package/src/utils/sdk-stream-timeout.ts +43 -0
- package/src/utils/sse-debug.ts +289 -0
- package/src/utils/stream-markup-healing.ts +612 -0
- package/src/utils/tool-choice.ts +99 -0
- package/src/utils/validation.ts +1024 -0
- package/src/utils.ts +166 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export type HeadersLike = Headers | Record<string, string | undefined> | undefined | null;
|
|
2
|
+
|
|
3
|
+
const RETRY_AFTER_HINT = "retry-after-ms=";
|
|
4
|
+
|
|
5
|
+
export function formatErrorMessageWithRetryAfter(error: unknown, headers?: HeadersLike): string {
|
|
6
|
+
const message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
7
|
+
if (message.includes(RETRY_AFTER_HINT)) {
|
|
8
|
+
return message;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const retryAfterMs = getRetryAfterMsFromHeaders(headers ?? getHeadersFromError(error));
|
|
12
|
+
if (retryAfterMs === undefined) {
|
|
13
|
+
return message;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return `${message} ${RETRY_AFTER_HINT}${retryAfterMs}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getRetryAfterMsFromHeaders(headers: HeadersLike): number | undefined {
|
|
20
|
+
if (!headers) return undefined;
|
|
21
|
+
|
|
22
|
+
const retryAfter = parseRetryAfterHeader(getHeaderValue(headers, "retry-after"));
|
|
23
|
+
const resetMs = parseResetHeader(getHeaderValue(headers, "x-ratelimit-reset-ms"), "ms");
|
|
24
|
+
const resetSeconds = parseResetHeader(getHeaderValue(headers, "x-ratelimit-reset"), "s");
|
|
25
|
+
|
|
26
|
+
const candidates = [retryAfter, resetMs, resetSeconds].filter((value): value is number => value !== undefined);
|
|
27
|
+
if (candidates.length === 0) return undefined;
|
|
28
|
+
return Math.max(...candidates);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getHeadersFromError(error: unknown): HeadersLike {
|
|
32
|
+
if (!error || typeof error !== "object") return undefined;
|
|
33
|
+
const record = error as { headers?: unknown; response?: { headers?: unknown }; cause?: unknown };
|
|
34
|
+
const direct = extractHeaders(record.headers) ?? extractHeaders(record.response?.headers);
|
|
35
|
+
if (direct) return direct;
|
|
36
|
+
if (record.cause) return getHeadersFromError(record.cause);
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function extractHeaders(value: unknown): HeadersLike {
|
|
41
|
+
if (!value) return undefined;
|
|
42
|
+
if (value instanceof Headers) return value;
|
|
43
|
+
if (typeof value === "object") return value as Record<string, string | undefined>;
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getHeaderValue(headers: Headers | Record<string, string | undefined>, name: string): string | undefined {
|
|
48
|
+
if (headers instanceof Headers) {
|
|
49
|
+
const value = headers.get(name);
|
|
50
|
+
return value ?? undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const target = name.toLowerCase();
|
|
54
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
55
|
+
if (key.toLowerCase() === target && typeof value === "string") {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseRetryAfterHeader(value: string | undefined): number | undefined {
|
|
63
|
+
if (!value) return undefined;
|
|
64
|
+
const trimmed = value.trim();
|
|
65
|
+
if (!trimmed) return undefined;
|
|
66
|
+
|
|
67
|
+
const numeric = Number(trimmed);
|
|
68
|
+
if (Number.isFinite(numeric)) {
|
|
69
|
+
if (numeric <= 0) return undefined;
|
|
70
|
+
return Math.ceil(numeric * 1000);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const dateMs = Date.parse(trimmed);
|
|
74
|
+
if (!Number.isNaN(dateMs)) {
|
|
75
|
+
const delay = dateMs - Date.now();
|
|
76
|
+
return delay > 0 ? Math.ceil(delay) : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseResetHeader(value: string | undefined, unit: "ms" | "s"): number | undefined {
|
|
83
|
+
if (!value) return undefined;
|
|
84
|
+
const numeric = Number(value);
|
|
85
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return undefined;
|
|
86
|
+
|
|
87
|
+
const nowMs = Date.now();
|
|
88
|
+
let targetMs: number | undefined;
|
|
89
|
+
|
|
90
|
+
if (unit === "ms") {
|
|
91
|
+
if (numeric > 1e12) {
|
|
92
|
+
targetMs = numeric;
|
|
93
|
+
} else if (numeric > 1e9) {
|
|
94
|
+
targetMs = numeric * 1000;
|
|
95
|
+
} else {
|
|
96
|
+
return Math.ceil(numeric);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (numeric > 1e12) {
|
|
100
|
+
targetMs = numeric;
|
|
101
|
+
} else if (numeric > 1e9) {
|
|
102
|
+
targetMs = numeric * 1000;
|
|
103
|
+
} else {
|
|
104
|
+
return Math.ceil(numeric * 1000);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (targetMs <= nowMs) return undefined;
|
|
109
|
+
return Math.ceil(targetMs - nowMs);
|
|
110
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { scheduler } from "node:timers/promises";
|
|
2
|
+
import { extractHttpStatusFromError, isRetryableError } from "@prometheus-ai/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot intermittently rejects preview models (gpt-5.3-codex,
|
|
6
|
+
* gpt-5.4, gpt-5.4-mini, ...) with HTTP 400 `model_not_supported`, even
|
|
7
|
+
* though the model is listed as enabled on the user's account via `/models`.
|
|
8
|
+
*
|
|
9
|
+
* Root cause: Copilot's request-routing backend is rolled out per OAuth
|
|
10
|
+
* client. Our OAuth client id is shared with opencode; VS Code uses its own
|
|
11
|
+
* client and sees full availability, so the same account may succeed in VS
|
|
12
|
+
* Code and flap between 200/400 here. See opencode#13313 and copilot-cli#2597.
|
|
13
|
+
*
|
|
14
|
+
* Retrying the identical request 2-3 times almost always lands on a backend
|
|
15
|
+
* that has the model, so we wrap the initial request with a short retry loop.
|
|
16
|
+
*/
|
|
17
|
+
export function isCopilotTransientModelError(error: unknown): boolean {
|
|
18
|
+
if (extractHttpStatusFromError(error) !== 400) return false;
|
|
19
|
+
if (!error || typeof error !== "object") return false;
|
|
20
|
+
const info = error as { code?: unknown; error?: { code?: unknown } | null };
|
|
21
|
+
const code = typeof info.code === "string" ? info.code : info.error?.code;
|
|
22
|
+
return code === "model_not_supported";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const COPILOT_MODEL_RETRY_MAX_ATTEMPTS = 3;
|
|
26
|
+
const COPILOT_MODEL_RETRY_BASE_DELAY_MS = 400;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wrap an initial Copilot request so transient `model_not_supported` 400s are
|
|
30
|
+
* retried a small number of times. No-op for non-Copilot providers.
|
|
31
|
+
*
|
|
32
|
+
* The callback **MUST** create a fresh in-flight request each invocation — a
|
|
33
|
+
* once-consumed AsyncIterable cannot be re-iterated.
|
|
34
|
+
*/
|
|
35
|
+
export async function callWithCopilotModelRetry<T>(
|
|
36
|
+
fn: () => Promise<T>,
|
|
37
|
+
options: { provider: string; signal?: AbortSignal; retryBaseDelayMs?: number },
|
|
38
|
+
): Promise<T> {
|
|
39
|
+
if (options.provider !== "github-copilot") return fn();
|
|
40
|
+
|
|
41
|
+
let lastError: unknown;
|
|
42
|
+
const retryBaseDelayMs = options.retryBaseDelayMs ?? COPILOT_MODEL_RETRY_BASE_DELAY_MS;
|
|
43
|
+
for (let attempt = 0; attempt < COPILOT_MODEL_RETRY_MAX_ATTEMPTS; attempt++) {
|
|
44
|
+
try {
|
|
45
|
+
return await fn();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
lastError = error;
|
|
48
|
+
if (!isCopilotTransientModelError(error) && !isRetryableError(error)) throw error;
|
|
49
|
+
if (attempt === COPILOT_MODEL_RETRY_MAX_ATTEMPTS - 1) break;
|
|
50
|
+
await scheduler.wait(retryBaseDelayMs * (attempt + 1), { signal: options.signal });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw lastError;
|
|
54
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Schema Constraints
|
|
2
|
+
|
|
3
|
+
This document is the operational contract for schema normalization/strictness in `packages/ai/src/utils/schema`.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
- Applies to provider-facing tool schemas produced by:
|
|
8
|
+
- `normalize.ts` — Google, CCA, MCP, OpenAI Responses, and OpenAI strict-mode (sanitize + enforce) sanitization. All schema walkers live here.
|
|
9
|
+
- `adapt.ts` — thin composer wrapping `tryEnforceStrictSchema` for provider call sites, plus the `PROMETHEUS_NO_STRICT` env flag callers consult to opt out of strict mode.
|
|
10
|
+
- `fields.ts` — keyword classification sets used by the walkers.
|
|
11
|
+
- Covers OpenAI-style strict mode, OpenAI Responses `oneOf` rejection, Google schema constraints, and Cloud Code Assist Claude constraints.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1) OpenAI-style strict mode (`adaptSchemaForStrict` / `tryEnforceStrictSchema`)
|
|
15
|
+
|
|
16
|
+
When strict mode is requested (`strict=true` at call site), the schema MUST satisfy all of the following after adaptation:
|
|
17
|
+
|
|
18
|
+
1. **Non-structural keywords are removed before strict enforcement**
|
|
19
|
+
- Sanitization uses `sanitizeSchemaForStrictMode`.
|
|
20
|
+
- Removed keys include formatting/validation/decorative keywords and unsupported structural extras:
|
|
21
|
+
- `format`, `pattern`, `minLength`, `maxLength`, `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
|
|
22
|
+
- `minItems`, `maxItems`, `uniqueItems`, `multipleOf`
|
|
23
|
+
- `$schema`, `examples`, `default`, `title`, `$comment`
|
|
24
|
+
- `if`, `then`, `else`, `not`
|
|
25
|
+
- `unevaluatedProperties`, `unevaluatedItems`, `patternProperties`
|
|
26
|
+
- `propertyNames`, `contains`, `minContains`, `maxContains`
|
|
27
|
+
- `dependentRequired`, `dependentSchemas`
|
|
28
|
+
- `contentEncoding`, `contentMediaType`, `contentSchema`
|
|
29
|
+
- `deprecated`, `readOnly`, `writeOnly`
|
|
30
|
+
- `minProperties`, `maxProperties`
|
|
31
|
+
- `$dynamicRef`, `$dynamicAnchor`
|
|
32
|
+
- Before stripping `default`, its value is inlined into the sibling `description` as ` (default: X)` so that strict-mode providers retain the default hint in free-form text. Inlining is skipped when `description` already contains `(default:` or when no sibling `description` is present.
|
|
33
|
+
|
|
34
|
+
2. **`const` is normalized to `enum`**
|
|
35
|
+
- If a node contains `const`, strict sanitization converts it to `enum: [const]`.
|
|
36
|
+
|
|
37
|
+
3. **Object and tuple strictness is enforced recursively**
|
|
38
|
+
- Every object node gets `additionalProperties: false`.
|
|
39
|
+
- Every property key is included in `required`.
|
|
40
|
+
- Optional properties are wrapped as nullable unions:
|
|
41
|
+
- `anyOf: [<original schema>, { "type": "null" }]`.
|
|
42
|
+
- Tuple entries in `prefixItems` are strictified recursively.
|
|
43
|
+
|
|
44
|
+
4. **Schema nodes must be representable in strict mode**
|
|
45
|
+
- Nodes without `type`, combinator, `$ref`, or `not` are invalid in strict enforcement and MUST throw.
|
|
46
|
+
- Example invalid node: `{}` or `{ items: {} }`.
|
|
47
|
+
|
|
48
|
+
5. **Failure mode is fail-open to non-strict**
|
|
49
|
+
- `tryEnforceStrictSchema` MUST return `{ strict: false, schema: original }` when strict enforcement throws.
|
|
50
|
+
- It MUST NOT emit partially-broken strict schema.
|
|
51
|
+
|
|
52
|
+
6. **Provider payload strict flag must match effective strictness**
|
|
53
|
+
- Callers MUST send `strict: true` only if enforcement succeeded (`effectiveStrict === true`).
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2) Google Gemini / Vertex / Gemini CLI (`normalizeSchemaForGoogle`)
|
|
58
|
+
|
|
59
|
+
Schemas sent on the Google JSON Schema path MUST follow:
|
|
60
|
+
|
|
61
|
+
1. **Unsupported JSON Schema keywords are stripped (except property names under `properties`)**
|
|
62
|
+
- Unsupported keys (`UNSUPPORTED_SCHEMA_FIELDS`):
|
|
63
|
+
- `$schema`, `$ref`, `$defs`, `$dynamicRef`, `$dynamicAnchor`
|
|
64
|
+
- `examples`, `prefixItems`, `unevaluatedProperties`, `unevaluatedItems`
|
|
65
|
+
- `patternProperties`, `additionalProperties`
|
|
66
|
+
- `minItems`, `maxItems`, `minLength`, `maxLength`
|
|
67
|
+
- `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
|
|
68
|
+
- `pattern`, `format`
|
|
69
|
+
- Important: keys inside a `properties` object are treated as property names and MUST NOT be stripped by keyword match.
|
|
70
|
+
- Human-meaningful stripped keys (`pattern`, `format`, min/max constraints, `default`, `examples`, etc.) are appended to the sibling `description` as an Anthropic-style spill block: `{pattern: "^foo$", minimum: 0}`. Structural/meta keys such as `$ref`, `$defs`, and `additionalProperties` are not spilled.
|
|
71
|
+
|
|
72
|
+
2. **`type` arrays are normalized to scalar type + nullable marker**
|
|
73
|
+
- `type: ["T", "null"]` becomes `type: "T"` and `nullable: true`.
|
|
74
|
+
- Google expects scalar type, not `type[]`.
|
|
75
|
+
|
|
76
|
+
3. **`const` is converted to `enum`**
|
|
77
|
+
- If `const` exists, schema uses/merges `enum` with the const value.
|
|
78
|
+
|
|
79
|
+
4. **Object schemas get an explicit properties map**
|
|
80
|
+
- `{ "type": "object" }` becomes `{ "type": "object", "properties": {} }`.
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3) Claude via Cloud Code Assist (`normalizeSchemaForCCA`)
|
|
84
|
+
|
|
85
|
+
For Cloud Code Assist Claude tool declarations, schema MUST satisfy stricter constraints than generic Google path.
|
|
86
|
+
|
|
87
|
+
### 3.1 Transport contract
|
|
88
|
+
|
|
89
|
+
1. **Use legacy `parameters` field** (not `parametersJsonSchema`) for CCA Claude.
|
|
90
|
+
2. CCA path uses the full `normalizeSchemaForCCA` pipeline.
|
|
91
|
+
|
|
92
|
+
### 3.2 Sanitization contract
|
|
93
|
+
|
|
94
|
+
1. Start with Google unsupported-key stripping behavior.
|
|
95
|
+
2. **`nullable` keyword MUST be stripped** in CCA Claude path.
|
|
96
|
+
3. `type: ["T", "null"]` becomes `type: "T"` with no `nullable` marker.
|
|
97
|
+
4. Human-meaningful stripped keys are appended to `description` with the same spill format used by the Google dispatcher.
|
|
98
|
+
|
|
99
|
+
### 3.3 Combiner/union normalization contract
|
|
100
|
+
|
|
101
|
+
1. Object-only `anyOf`/`oneOf` variants SHOULD be merged into a single object shape where safe.
|
|
102
|
+
2. Same-type combiner variants SHOULD be collapsed to one schema.
|
|
103
|
+
3. Mixed-type combiner variants MAY be lossy-collapsed to first non-null scalar type when required for CCA acceptance.
|
|
104
|
+
4. Residual combiners are recursively stripped where collapsible (`stripResidualCombiners`).
|
|
105
|
+
|
|
106
|
+
### 3.4 Nullable property normalization contract
|
|
107
|
+
|
|
108
|
+
1. Property-local nullability expressed as:
|
|
109
|
+
- `nullable: true`, or
|
|
110
|
+
- `type` union including `null`, or
|
|
111
|
+
- `anyOf`/`oneOf` with one `{ "type": "null" }` branch
|
|
112
|
+
MUST be converted to non-required property semantics where possible.
|
|
113
|
+
2. If a property is detected nullable after normalization, it MUST be removed from `required`.
|
|
114
|
+
|
|
115
|
+
### 3.5 Residual incompatibility gate (hard stop)
|
|
116
|
+
|
|
117
|
+
After normalization, schema MUST NOT contain any of:
|
|
118
|
+
|
|
119
|
+
- `type` as array
|
|
120
|
+
- `type: "null"`
|
|
121
|
+
- `nullable` key
|
|
122
|
+
- `anyOf`, `oneOf`, `allOf` arrays
|
|
123
|
+
|
|
124
|
+
If any remain, schema is incompatible.
|
|
125
|
+
|
|
126
|
+
### 3.6 Validation + fallback contract
|
|
127
|
+
|
|
128
|
+
1. Normalized schema is validated with AJV 2020 schema validation.
|
|
129
|
+
2. If invalid OR residual incompatibilities exist, output MUST fallback to:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{ "type": "object", "properties": {} }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
3. Fallback is per-tool and fail-open; one bad tool schema MUST NOT fail the whole request.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 4) Practical provider mapping
|
|
140
|
+
|
|
141
|
+
- **OpenAI-compatible strict paths** (`openai-completions`, `openai-responses`, `openai-codex-responses`):
|
|
142
|
+
- Use `adaptSchemaForStrict`.
|
|
143
|
+
- Emit `strict: true` only when effective strict enforcement succeeded.
|
|
144
|
+
|
|
145
|
+
- **Google Gemini/Vertex/Gemini CLI (non-CCA Claude)**:
|
|
146
|
+
- Use `normalizeSchemaForGoogle` and send schema on `parametersJsonSchema` path.
|
|
147
|
+
|
|
148
|
+
- **Cloud Code Assist Claude models (`model.id` starts with `claude-`)**:
|
|
149
|
+
- Use `normalizeSchemaForCCA` and send sanitized normalized schema in `parameters`.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 5) Maintenance rules
|
|
154
|
+
|
|
155
|
+
When adding/changing provider adapters:
|
|
156
|
+
|
|
157
|
+
1. Any new unsupported keyword MUST be added to the appropriate set in `fields.ts`.
|
|
158
|
+
2. Any new normalization rule MUST include regression tests under `packages/ai/test`.
|
|
159
|
+
3. Never bypass adapter helpers (`adaptSchemaForStrict`, `normalizeSchemaForGoogle`, `normalizeSchemaForCCA`, `normalizeSchemaForMCP`) in provider code.
|
|
160
|
+
4. If a provider rejects schema with partial support, prefer deterministic per-tool fallback over request-wide failure.
|
|
161
|
+
|
|
162
|
+
## 6) Gemini CLI / Antigravity CCA parity
|
|
163
|
+
|
|
164
|
+
The Gemini CLI / Antigravity Claude path MUST run the same full `normalizeSchemaForCCA` pipeline as the shared Google Claude path. It MUST NOT call only the first keyword-stripping pass, because that leaves object combiners, nullable unions, residual combiners, and fallback gating inconsistent between transports.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { $flag } from "@prometheus-ai/utils";
|
|
2
|
+
import { upgradeJsonSchemaTo202012 } from "./draft";
|
|
3
|
+
import { tryEnforceStrictSchema } from "./normalize";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Set when callers want to globally bypass OpenAI strict-mode enforcement
|
|
7
|
+
* (e.g. for debugging a provider that misreports strict support, or when
|
|
8
|
+
* comparing strict vs non-strict outputs).
|
|
9
|
+
*
|
|
10
|
+
* Honored by every provider that emits `strict: true` on its function tools —
|
|
11
|
+
* see `openai-completions`, `openai-responses`, `openai-codex-responses`, and
|
|
12
|
+
* the strict candidate selection in `anthropic`.
|
|
13
|
+
*/
|
|
14
|
+
export const NO_STRICT = $flag("PROMETHEUS_NO_STRICT");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Consolidated helper for OpenAI-style strict schema enforcement.
|
|
18
|
+
*
|
|
19
|
+
* Each provider computes its own `strict` boolean (logic differs), then calls
|
|
20
|
+
* this to handle the tryEnforceStrictSchema dance uniformly:
|
|
21
|
+
* - Draft-07-shaped inputs are upgraded to draft 2020-12 first.
|
|
22
|
+
* - If `strict` is false, passes the upgraded schema through unchanged.
|
|
23
|
+
* - If `strict` is true, attempts to enforce strict mode; falls back to
|
|
24
|
+
* non-strict if the schema isn't representable.
|
|
25
|
+
*/
|
|
26
|
+
export function adaptSchemaForStrict(
|
|
27
|
+
schema: Record<string, unknown>,
|
|
28
|
+
strict: boolean,
|
|
29
|
+
): { schema: Record<string, unknown>; strict: boolean } {
|
|
30
|
+
const upgraded = upgradeJsonSchemaTo202012(schema) as Record<string, unknown>;
|
|
31
|
+
if (!strict) {
|
|
32
|
+
return { schema: upgraded, strict: false };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return tryEnforceStrictSchema(upgraded);
|
|
36
|
+
}
|