@fractaal/pi-ai 0.85.4 → 0.86.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 +322 -46
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +403 -140
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/anthropic-messages.lazy.d.ts.map +1 -1
- package/dist/api/azure-openai-config.d.ts +15 -0
- package/dist/api/azure-openai-config.d.ts.map +1 -0
- package/dist/api/azure-openai-config.js +76 -0
- package/dist/api/azure-openai-config.js.map +1 -0
- package/dist/api/azure-openai-responses.d.ts +5 -6
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +42 -86
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +233 -24
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
- package/dist/api/cloudflare-ai-binding.d.ts +77 -0
- package/dist/api/cloudflare-ai-binding.d.ts.map +1 -0
- package/dist/api/cloudflare-ai-binding.js +72 -0
- package/dist/api/cloudflare-ai-binding.js.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.js +48 -0
- package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.js.map +1 -0
- package/dist/api/cloudflare.d.ts +2 -0
- package/dist/api/cloudflare.d.ts.map +1 -1
- package/dist/api/cloudflare.js +2 -0
- package/dist/api/cloudflare.js.map +1 -1
- package/dist/api/constrained-sampling.d.ts +10 -1
- package/dist/api/constrained-sampling.d.ts.map +1 -1
- package/dist/api/constrained-sampling.js +137 -4
- package/dist/api/constrained-sampling.js.map +1 -1
- package/dist/api/github-copilot-headers.d.ts.map +1 -1
- package/dist/api/github-copilot-headers.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts +2 -2
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +39 -82
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-generative-ai.lazy.d.ts.map +1 -1
- package/dist/api/google-shared.d.ts +27 -5
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +126 -18
- package/dist/api/google-shared.js.map +1 -1
- package/dist/api/google-vertex.d.ts +2 -2
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +42 -73
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/google-vertex.lazy.d.ts.map +1 -1
- package/dist/api/lazy.d.ts +5 -1
- package/dist/api/lazy.d.ts.map +1 -1
- package/dist/api/lazy.js +19 -7
- package/dist/api/lazy.js.map +1 -1
- package/dist/api/llama-cpp-classify.d.ts +33 -0
- package/dist/api/llama-cpp-classify.d.ts.map +1 -0
- package/dist/api/llama-cpp-classify.js +365 -0
- package/dist/api/llama-cpp-classify.js.map +1 -0
- package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
- package/dist/api/llama-cpp-classify.lazy.d.ts.map +1 -0
- package/dist/api/llama-cpp-classify.lazy.js +4 -0
- package/dist/api/llama-cpp-classify.lazy.js.map +1 -0
- package/dist/api/mistral-conversations.d.ts +2 -2
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +250 -69
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/mistral-conversations.lazy.d.ts.map +1 -1
- package/dist/api/openai-codex-compaction-history.d.ts +2 -2
- package/dist/api/openai-codex-compaction-history.d.ts.map +1 -1
- package/dist/api/openai-codex-compaction-history.js +1 -2
- package/dist/api/openai-codex-compaction-history.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +107 -60
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.lazy.js.map +1 -1
- package/dist/api/openai-completions.d.ts +11 -4
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +250 -134
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-completions.lazy.d.ts.map +1 -1
- package/dist/api/openai-prompt-cache.d.ts.map +1 -1
- package/dist/api/openai-prompt-cache.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts +8 -4
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +114 -62
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +55 -23
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openai-responses.lazy.d.ts.map +1 -1
- package/dist/api/openrouter-images.d.ts +2 -1
- package/dist/api/openrouter-images.d.ts.map +1 -1
- package/dist/api/openrouter-images.js +1 -0
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/openrouter-images.lazy.d.ts.map +1 -1
- package/dist/api/openrouter-images.lazy.js.map +1 -1
- package/dist/api/pi-messages.d.ts +5 -3
- package/dist/api/pi-messages.d.ts.map +1 -1
- package/dist/api/pi-messages.js +10 -3
- package/dist/api/pi-messages.js.map +1 -1
- package/dist/api/pi-messages.lazy.d.ts.map +1 -1
- package/dist/api/simple-options.d.ts +10 -3
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +31 -11
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +23 -0
- package/dist/api/system-one-shared.d.ts.map +1 -0
- package/dist/api/system-one-shared.js +183 -0
- package/dist/api/system-one-shared.js.map +1 -0
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +21 -7
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/api/typesafe-system-one.d.ts +4 -0
- package/dist/api/typesafe-system-one.d.ts.map +1 -0
- package/dist/api/typesafe-system-one.js +19 -0
- package/dist/api/typesafe-system-one.js.map +1 -0
- package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
- package/dist/api/typesafe-system-one.lazy.d.ts.map +1 -0
- package/dist/api/typesafe-system-one.lazy.js +4 -0
- package/dist/api/typesafe-system-one.lazy.js.map +1 -0
- package/dist/auth/context.d.ts.map +1 -1
- package/dist/auth/context.js.map +1 -1
- package/dist/auth/credential-store.d.ts +6 -6
- package/dist/auth/credential-store.d.ts.map +1 -1
- package/dist/auth/credential-store.js +22 -11
- package/dist/auth/credential-store.js.map +1 -1
- package/dist/auth/helpers.d.ts +1 -0
- package/dist/auth/helpers.d.ts.map +1 -1
- package/dist/auth/helpers.js +8 -3
- package/dist/auth/helpers.js.map +1 -1
- package/dist/auth/oauth/anthropic.d.ts.map +1 -1
- package/dist/auth/oauth/anthropic.js +78 -131
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/auth/oauth/callback-server.d.ts +55 -0
- package/dist/auth/oauth/callback-server.d.ts.map +1 -0
- package/dist/auth/oauth/callback-server.js +146 -0
- package/dist/auth/oauth/callback-server.js.map +1 -0
- package/dist/auth/oauth/device-code.d.ts +2 -1
- package/dist/auth/oauth/device-code.d.ts.map +1 -1
- package/dist/auth/oauth/device-code.js +5 -5
- package/dist/auth/oauth/device-code.js.map +1 -1
- package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
- package/dist/auth/oauth/github-copilot.js +121 -41
- package/dist/auth/oauth/github-copilot.js.map +1 -1
- package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
- package/dist/auth/oauth/kimi-coding.js +5 -6
- package/dist/auth/oauth/kimi-coding.js.map +1 -1
- package/dist/auth/oauth/load.d.ts +4 -0
- package/dist/auth/oauth/load.d.ts.map +1 -1
- package/dist/auth/oauth/load.js +10 -0
- package/dist/auth/oauth/load.js.map +1 -1
- package/dist/auth/oauth/meta.d.ts +17 -0
- package/dist/auth/oauth/meta.d.ts.map +1 -0
- package/dist/auth/oauth/meta.js +190 -0
- package/dist/auth/oauth/meta.js.map +1 -0
- package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
- package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -0
- package/dist/auth/oauth/openai-chatgpt.js +263 -0
- package/dist/auth/oauth/openai-chatgpt.js.map +1 -0
- package/dist/auth/oauth/openai-codex-errors.d.ts.map +1 -1
- package/dist/auth/oauth/openai-codex-errors.js.map +1 -1
- package/dist/auth/oauth/openai-codex.d.ts +1 -1
- package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
- package/dist/auth/oauth/openai-codex.js +23 -152
- package/dist/auth/oauth/openai-codex.js.map +1 -1
- package/dist/auth/oauth/openrouter.d.ts +1 -1
- package/dist/auth/oauth/openrouter.d.ts.map +1 -1
- package/dist/auth/oauth/openrouter.js +25 -144
- package/dist/auth/oauth/openrouter.js.map +1 -1
- package/dist/auth/oauth/pkce.d.ts.map +1 -1
- package/dist/auth/oauth/pkce.js.map +1 -1
- package/dist/auth/oauth/radius.d.ts +1 -1
- package/dist/auth/oauth/radius.d.ts.map +1 -1
- package/dist/auth/oauth/radius.js +26 -93
- package/dist/auth/oauth/radius.js.map +1 -1
- package/dist/auth/oauth/xai.d.ts.map +1 -1
- package/dist/auth/oauth/xai.js +3 -2
- package/dist/auth/oauth/xai.js.map +1 -1
- package/dist/auth/resolve.d.ts +19 -9
- package/dist/auth/resolve.d.ts.map +1 -1
- package/dist/auth/resolve.js +72 -53
- package/dist/auth/resolve.js.map +1 -1
- package/dist/auth/types.d.ts +29 -8
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/bedrock-provider.d.ts.map +1 -1
- package/dist/bun-oauth.d.ts.map +1 -1
- package/dist/bun-oauth.js +4 -0
- package/dist/bun-oauth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/compat/extension-oauth-types.d.ts.map +1 -1
- package/dist/compat.d.ts +3 -3
- package/dist/compat.d.ts.map +1 -1
- package/dist/compat.js +9 -6
- package/dist/compat.js.map +1 -1
- package/dist/env-api-keys.d.ts +5 -0
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +10 -1
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.d.ts +19 -8
- package/dist/image-models.d.ts.map +1 -1
- package/dist/image-models.js +14 -13
- package/dist/image-models.js.map +1 -1
- package/dist/images-api-registry.d.ts +7 -7
- package/dist/images-api-registry.d.ts.map +1 -1
- package/dist/images-api-registry.js.map +1 -1
- package/dist/images.d.ts +7 -2
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +5 -0
- package/dist/images.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/legacy-api-aliases.d.ts.map +1 -1
- package/dist/model-catalog.d.ts +25 -9
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +14 -2
- package/dist/model-catalog.js.map +1 -1
- package/dist/models-store.d.ts +12 -14
- package/dist/models-store.d.ts.map +1 -1
- package/dist/models-store.js +6 -3
- package/dist/models-store.js.map +1 -1
- package/dist/models.d.ts +138 -47
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +136 -38
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +136 -38
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +390 -104
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/providers/all.d.ts +19 -13
- package/dist/providers/all.d.ts.map +1 -1
- package/dist/providers/all.js +33 -23
- package/dist/providers/all.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +15 -7
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/amazon-bedrock.models.d.ts +4 -2
- package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.models.js +4 -2
- package/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/dist/providers/ant-ling.d.ts.map +1 -1
- package/dist/providers/ant-ling.js.map +1 -1
- package/dist/providers/ant-ling.models.d.ts +4 -2
- package/dist/providers/ant-ling.models.d.ts.map +1 -1
- package/dist/providers/ant-ling.models.js +4 -2
- package/dist/providers/ant-ling.models.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +39 -8
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/anthropic.models.d.ts +4 -2
- package/dist/providers/anthropic.models.d.ts.map +1 -1
- package/dist/providers/anthropic.models.js +4 -2
- package/dist/providers/anthropic.models.js.map +1 -1
- package/dist/providers/azure.d.ts +3 -0
- package/dist/providers/azure.d.ts.map +1 -0
- package/dist/providers/azure.js +46 -0
- package/dist/providers/azure.js.map +1 -0
- package/dist/providers/azure.models.d.ts +6 -0
- package/dist/providers/azure.models.d.ts.map +1 -0
- package/dist/providers/azure.models.js +8 -0
- package/dist/providers/azure.models.js.map +1 -0
- package/dist/providers/baseten.d.ts +3 -0
- package/dist/providers/baseten.d.ts.map +1 -0
- package/dist/providers/baseten.js +15 -0
- package/dist/providers/baseten.js.map +1 -0
- package/dist/providers/baseten.models.d.ts +6 -0
- package/dist/providers/baseten.models.d.ts.map +1 -0
- package/dist/providers/baseten.models.js +8 -0
- package/dist/providers/baseten.models.js.map +1 -0
- package/dist/providers/cerebras.d.ts.map +1 -1
- package/dist/providers/cerebras.js.map +1 -1
- package/dist/providers/cerebras.models.d.ts +4 -2
- package/dist/providers/cerebras.models.d.ts.map +1 -1
- package/dist/providers/cerebras.models.js +4 -2
- package/dist/providers/cerebras.models.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.d.ts +3 -1
- package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.js +4 -0
- package/dist/providers/cloudflare-ai-gateway.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
- package/dist/providers/cloudflare-auth.d.ts.map +1 -1
- package/dist/providers/cloudflare-auth.js +15 -10
- package/dist/providers/cloudflare-auth.js.map +1 -1
- package/dist/providers/cloudflare-stream.d.ts +6 -2
- package/dist/providers/cloudflare-stream.d.ts.map +1 -1
- package/dist/providers/cloudflare-stream.js +6 -0
- package/dist/providers/cloudflare-stream.js.map +1 -1
- package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -1
- package/dist/providers/cloudflare-workers-ai.js +10 -3
- package/dist/providers/cloudflare-workers-ai.js.map +1 -1
- package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
- package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -1
- package/dist/providers/cloudflare-workers-ai.models.js +4 -2
- package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
- package/dist/providers/data/.manifest.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.json +1 -0
- package/dist/providers/data/baseten.json +1 -0
- package/dist/providers/data/cerebras.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/kimi-coding.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-codex.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/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 -0
- 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/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.d.ts.map +1 -1
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/deepseek.models.d.ts +4 -2
- package/dist/providers/deepseek.models.d.ts.map +1 -1
- package/dist/providers/deepseek.models.js +4 -2
- package/dist/providers/deepseek.models.js.map +1 -1
- package/dist/providers/faux.d.ts +19 -13
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +109 -18
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/fireworks.d.ts.map +1 -1
- package/dist/providers/fireworks.js.map +1 -1
- package/dist/providers/fireworks.models.d.ts +4 -2
- package/dist/providers/fireworks.models.d.ts.map +1 -1
- package/dist/providers/fireworks.models.js +4 -2
- package/dist/providers/fireworks.models.js.map +1 -1
- package/dist/providers/github-copilot.d.ts.map +1 -1
- package/dist/providers/github-copilot.js +1 -1
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/github-copilot.models.d.ts +4 -2
- package/dist/providers/github-copilot.models.d.ts.map +1 -1
- package/dist/providers/github-copilot.models.js +4 -2
- package/dist/providers/github-copilot.models.js.map +1 -1
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +15 -7
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google-vertex.models.d.ts +4 -2
- package/dist/providers/google-vertex.models.d.ts.map +1 -1
- package/dist/providers/google-vertex.models.js +4 -2
- package/dist/providers/google-vertex.models.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/google.models.d.ts +4 -2
- package/dist/providers/google.models.d.ts.map +1 -1
- package/dist/providers/google.models.js +4 -2
- package/dist/providers/google.models.js.map +1 -1
- package/dist/providers/groq.d.ts.map +1 -1
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/groq.models.d.ts +4 -2
- package/dist/providers/groq.models.d.ts.map +1 -1
- package/dist/providers/groq.models.js +4 -2
- package/dist/providers/groq.models.js.map +1 -1
- package/dist/providers/huggingface.d.ts.map +1 -1
- package/dist/providers/huggingface.js.map +1 -1
- package/dist/providers/huggingface.models.d.ts +4 -2
- package/dist/providers/huggingface.models.d.ts.map +1 -1
- package/dist/providers/huggingface.models.js +4 -2
- package/dist/providers/huggingface.models.js.map +1 -1
- package/dist/providers/images/register-builtins.d.ts +1 -1
- package/dist/providers/images/register-builtins.d.ts.map +1 -1
- package/dist/providers/images/register-builtins.js.map +1 -1
- package/dist/providers/kimi-coding.d.ts.map +1 -1
- package/dist/providers/kimi-coding.js +1 -0
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/kimi-coding.models.d.ts +4 -2
- package/dist/providers/kimi-coding.models.d.ts.map +1 -1
- package/dist/providers/kimi-coding.models.js +4 -2
- package/dist/providers/kimi-coding.models.js.map +1 -1
- package/dist/providers/meta.d.ts +3 -0
- package/dist/providers/meta.d.ts.map +1 -0
- package/dist/providers/meta.js +24 -0
- package/dist/providers/meta.js.map +1 -0
- package/dist/providers/meta.models.d.ts +6 -0
- package/dist/providers/meta.models.d.ts.map +1 -0
- package/dist/providers/meta.models.js +8 -0
- package/dist/providers/meta.models.js.map +1 -0
- package/dist/providers/minimax-cn.d.ts.map +1 -1
- package/dist/providers/minimax-cn.js.map +1 -1
- package/dist/providers/minimax-cn.models.d.ts +4 -2
- package/dist/providers/minimax-cn.models.d.ts.map +1 -1
- package/dist/providers/minimax-cn.models.js +4 -2
- package/dist/providers/minimax-cn.models.js.map +1 -1
- package/dist/providers/minimax.d.ts.map +1 -1
- package/dist/providers/minimax.js.map +1 -1
- package/dist/providers/minimax.models.d.ts +4 -2
- package/dist/providers/minimax.models.d.ts.map +1 -1
- package/dist/providers/minimax.models.js +4 -2
- package/dist/providers/minimax.models.js.map +1 -1
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/mistral.models.d.ts +4 -2
- package/dist/providers/mistral.models.d.ts.map +1 -1
- package/dist/providers/mistral.models.js +4 -2
- package/dist/providers/mistral.models.js.map +1 -1
- package/dist/providers/moonshotai-cn.d.ts.map +1 -1
- package/dist/providers/moonshotai-cn.js.map +1 -1
- package/dist/providers/moonshotai-cn.models.d.ts +4 -2
- package/dist/providers/moonshotai-cn.models.d.ts.map +1 -1
- package/dist/providers/moonshotai-cn.models.js +4 -2
- package/dist/providers/moonshotai-cn.models.js.map +1 -1
- package/dist/providers/moonshotai.d.ts.map +1 -1
- package/dist/providers/moonshotai.js.map +1 -1
- package/dist/providers/moonshotai.models.d.ts +4 -2
- package/dist/providers/moonshotai.models.d.ts.map +1 -1
- package/dist/providers/moonshotai.models.js +4 -2
- package/dist/providers/moonshotai.models.js.map +1 -1
- package/dist/providers/nvidia.d.ts.map +1 -1
- package/dist/providers/nvidia.js.map +1 -1
- package/dist/providers/nvidia.models.d.ts +4 -2
- package/dist/providers/nvidia.models.d.ts.map +1 -1
- package/dist/providers/nvidia.models.js +4 -2
- package/dist/providers/nvidia.models.js.map +1 -1
- package/dist/providers/openai-codex-catalog.d.ts +1 -0
- package/dist/providers/openai-codex-catalog.d.ts.map +1 -1
- package/dist/providers/openai-codex-catalog.js +19 -10
- package/dist/providers/openai-codex-catalog.js.map +1 -1
- package/dist/providers/openai-codex.d.ts.map +1 -1
- package/dist/providers/openai-codex.js +5 -1
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/openai-codex.models.d.ts +4 -2
- package/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/dist/providers/openai-codex.models.js +4 -2
- package/dist/providers/openai-codex.models.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +11 -2
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai.models.d.ts +4 -2
- package/dist/providers/openai.models.d.ts.map +1 -1
- package/dist/providers/openai.models.js +4 -2
- package/dist/providers/openai.models.js.map +1 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +5 -4
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/opencode-go.models.d.ts +4 -2
- package/dist/providers/opencode-go.models.d.ts.map +1 -1
- package/dist/providers/opencode-go.models.js +4 -2
- package/dist/providers/opencode-go.models.js.map +1 -1
- package/dist/providers/opencode-headers.d.ts +4 -0
- package/dist/providers/opencode-headers.d.ts.map +1 -0
- package/dist/providers/opencode-headers.js +22 -0
- package/dist/providers/opencode-headers.js.map +1 -0
- package/dist/providers/opencode.d.ts +3 -1
- package/dist/providers/opencode.d.ts.map +1 -1
- package/dist/providers/opencode.js +10 -6
- package/dist/providers/opencode.js.map +1 -1
- package/dist/providers/opencode.models.d.ts +4 -2
- package/dist/providers/opencode.models.d.ts.map +1 -1
- package/dist/providers/opencode.models.js +4 -2
- package/dist/providers/opencode.models.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +16 -3
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/openrouter.models.d.ts +4 -2
- package/dist/providers/openrouter.models.d.ts.map +1 -1
- package/dist/providers/openrouter.models.js +4 -2
- package/dist/providers/openrouter.models.js.map +1 -1
- package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -1
- package/dist/providers/qwen-token-plan-cn.js.map +1 -1
- package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -1
- package/dist/providers/qwen-token-plan-cn.models.js +4 -2
- package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
- package/dist/providers/qwen-token-plan-individual.d.ts +3 -0
- package/dist/providers/qwen-token-plan-individual.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-individual.js +15 -0
- package/dist/providers/qwen-token-plan-individual.js.map +1 -0
- package/dist/providers/qwen-token-plan-individual.models.d.ts +6 -0
- package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-individual.models.js +8 -0
- package/dist/providers/qwen-token-plan-individual.models.js.map +1 -0
- package/dist/providers/qwen-token-plan.d.ts.map +1 -1
- package/dist/providers/qwen-token-plan.js.map +1 -1
- package/dist/providers/qwen-token-plan.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan.models.d.ts.map +1 -1
- package/dist/providers/qwen-token-plan.models.js +4 -2
- package/dist/providers/qwen-token-plan.models.js.map +1 -1
- package/dist/providers/radius-config.d.ts.map +1 -1
- package/dist/providers/radius-config.js.map +1 -1
- package/dist/providers/radius.d.ts.map +1 -1
- package/dist/providers/radius.js +53 -29
- package/dist/providers/radius.js.map +1 -1
- package/dist/providers/radius.models.d.ts +6 -0
- package/dist/providers/radius.models.d.ts.map +1 -0
- package/dist/providers/radius.models.js +8 -0
- package/dist/providers/radius.models.js.map +1 -0
- package/dist/providers/together.d.ts.map +1 -1
- package/dist/providers/together.js.map +1 -1
- package/dist/providers/together.models.d.ts +4 -2
- package/dist/providers/together.models.d.ts.map +1 -1
- package/dist/providers/together.models.js +4 -2
- package/dist/providers/together.models.js.map +1 -1
- package/dist/providers/typesafe.d.ts +3 -0
- package/dist/providers/typesafe.d.ts.map +1 -0
- package/dist/providers/typesafe.js +18 -0
- package/dist/providers/typesafe.js.map +1 -0
- package/dist/providers/typesafe.models.d.ts +6 -0
- package/dist/providers/typesafe.models.d.ts.map +1 -0
- package/dist/providers/typesafe.models.js +8 -0
- package/dist/providers/typesafe.models.js.map +1 -0
- package/dist/providers/vercel-ai-gateway.d.ts.map +1 -1
- package/dist/providers/vercel-ai-gateway.js +5 -2
- package/dist/providers/vercel-ai-gateway.js.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
- package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.js +4 -2
- package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.d.ts.map +1 -1
- package/dist/providers/xai.js +2 -5
- package/dist/providers/xai.js.map +1 -1
- package/dist/providers/xai.models.d.ts +4 -2
- package/dist/providers/xai.models.d.ts.map +1 -1
- package/dist/providers/xai.models.js +4 -2
- package/dist/providers/xai.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/dist/providers/xiaomi.d.ts.map +1 -1
- package/dist/providers/xiaomi.js.map +1 -1
- package/dist/providers/xiaomi.models.d.ts +4 -2
- package/dist/providers/xiaomi.models.d.ts.map +1 -1
- package/dist/providers/xiaomi.models.js +4 -2
- package/dist/providers/xiaomi.models.js.map +1 -1
- package/dist/providers/zai-coding-cn.d.ts.map +1 -1
- package/dist/providers/zai-coding-cn.js.map +1 -1
- package/dist/providers/zai-coding-cn.models.d.ts +4 -2
- package/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
- package/dist/providers/zai-coding-cn.models.js +4 -2
- package/dist/providers/zai-coding-cn.models.js.map +1 -1
- package/dist/providers/zai.d.ts.map +1 -1
- package/dist/providers/zai.js.map +1 -1
- package/dist/providers/zai.models.d.ts +4 -2
- package/dist/providers/zai.models.d.ts.map +1 -1
- package/dist/providers/zai.models.js +4 -2
- package/dist/providers/zai.models.js.map +1 -1
- package/dist/session-resources.d.ts.map +1 -1
- package/dist/session-resources.js.map +1 -1
- package/dist/types.d.ts +414 -126
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/abort-signals.d.ts.map +1 -1
- package/dist/utils/abort-signals.js.map +1 -1
- package/dist/utils/abort.d.ts +8 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +49 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/assistant-message-frame.d.ts +77 -0
- package/dist/utils/assistant-message-frame.d.ts.map +1 -0
- package/dist/utils/assistant-message-frame.js +425 -0
- package/dist/utils/assistant-message-frame.js.map +1 -0
- package/dist/utils/diagnostics.d.ts +3 -2
- package/dist/utils/diagnostics.d.ts.map +1 -1
- package/dist/utils/diagnostics.js.map +1 -1
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +23 -4
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/estimate.d.ts +2 -2
- package/dist/utils/estimate.d.ts.map +1 -1
- package/dist/utils/estimate.js +8 -29
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -7
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/hash.d.ts.map +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/headers.d.ts +1 -1
- package/dist/utils/headers.d.ts.map +1 -1
- package/dist/utils/headers.js +10 -8
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/model-operations.d.ts +11 -0
- package/dist/utils/model-operations.d.ts.map +1 -0
- package/dist/utils/model-operations.js +47 -0
- package/dist/utils/model-operations.js.map +1 -0
- package/dist/utils/models-error.d.ts +8 -0
- package/dist/utils/models-error.d.ts.map +1 -0
- package/dist/utils/models-error.js +19 -0
- package/dist/utils/models-error.js.map +1 -0
- package/dist/utils/node-http-proxy.d.ts.map +1 -1
- package/dist/utils/node-http-proxy.js +53 -12
- package/dist/utils/node-http-proxy.js.map +1 -1
- package/dist/utils/oauth-page.d.ts.map +1 -0
- package/dist/{auth/oauth → utils}/oauth-page.js +1 -1
- package/dist/utils/oauth-page.js.map +1 -0
- package/dist/utils/overflow.d.ts +11 -1
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +24 -6
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/dist/utils/pi-user-agent.js +12 -0
- package/dist/utils/pi-user-agent.js.map +1 -0
- package/dist/utils/provider-env.d.ts.map +1 -1
- package/dist/utils/provider-env.js.map +1 -1
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +3 -2
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/retry.d.ts +6 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +23 -2
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/sanitize-unicode.d.ts.map +1 -1
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/sleep.d.ts +2 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +15 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/text.d.ts +9 -1
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/text.js +26 -0
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/transcript.d.ts +87 -0
- package/dist/utils/transcript.d.ts.map +1 -0
- package/dist/utils/transcript.js +207 -0
- package/dist/utils/transcript.js.map +1 -0
- package/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/dist/utils/typebox-helpers.js.map +1 -1
- package/dist/utils/uuid.d.ts +2 -2
- package/dist/utils/uuid.d.ts.map +1 -1
- package/dist/utils/uuid.js +35 -39
- package/dist/utils/uuid.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +40 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +21 -15
- package/dist/auth/oauth/oauth-page.d.ts.map +0 -1
- package/dist/auth/oauth/oauth-page.js.map +0 -1
- package/dist/image-models.generated.d.ts +0 -605
- package/dist/image-models.generated.d.ts.map +0 -1
- package/dist/image-models.generated.js +0 -607
- package/dist/image-models.generated.js.map +0 -1
- package/dist/images-models.d.ts +0 -95
- package/dist/images-models.d.ts.map +0 -1
- package/dist/images-models.js +0 -142
- package/dist/images-models.js.map +0 -1
- package/dist/providers/azure-openai-responses.d.ts +0 -3
- package/dist/providers/azure-openai-responses.d.ts.map +0 -1
- package/dist/providers/azure-openai-responses.js +0 -14
- package/dist/providers/azure-openai-responses.js.map +0 -1
- package/dist/providers/azure-openai-responses.models.d.ts +0 -4
- package/dist/providers/azure-openai-responses.models.d.ts.map +0 -1
- package/dist/providers/azure-openai-responses.models.js +0 -6
- package/dist/providers/azure-openai-responses.models.js.map +0 -1
- package/dist/providers/data/azure-openai-responses.json +0 -1
- package/dist/providers/openrouter-images.d.ts +0 -3
- package/dist/providers/openrouter-images.d.ts.map +0 -1
- package/dist/providers/openrouter-images.js +0 -22
- package/dist/providers/openrouter-images.js.map +0 -1
- package/dist/utils/deferred-tools.d.ts +0 -9
- package/dist/utils/deferred-tools.d.ts.map +0 -1
- package/dist/utils/deferred-tools.js +0 -36
- package/dist/utils/deferred-tools.js.map +0 -1
- /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Unified LLM API with provider collections, automatic auth resolution, token and cost tracking, and simple context persistence and hand-off to other models mid-session.
|
|
4
4
|
|
|
5
|
-
**Note**:
|
|
5
|
+
**Note**: The chat catalog only includes models that support tool calling (function calling), as this is essential for agentic workflows. Image and classifier catalogs use their operation-specific capabilities.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
@@ -26,8 +26,10 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
26
26
|
- [Streaming Tool Calls with Partial JSON](#streaming-tool-calls-with-partial-json)
|
|
27
27
|
- [Validating Tool Arguments](#validating-tool-arguments)
|
|
28
28
|
- [Complete Event Reference](#complete-event-reference)
|
|
29
|
+
- [Compact Assistant Message Frames](#compact-assistant-message-frames)
|
|
29
30
|
- [Image Input](#image-input)
|
|
30
31
|
- [Image Generation](#image-generation)
|
|
32
|
+
- [Classification](#classification)
|
|
31
33
|
- [Thinking/Reasoning](#thinkingreasoning)
|
|
32
34
|
- [Unified Interface](#unified-interface-streamsimplecompletesimple)
|
|
33
35
|
- [Provider-Specific Options](#provider-specific-options-streamcomplete)
|
|
@@ -37,12 +39,14 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
37
39
|
- [Aborting Requests](#aborting-requests)
|
|
38
40
|
- [Continuing After Abort](#continuing-after-abort)
|
|
39
41
|
- [Debugging Provider Payloads](#debugging-provider-payloads)
|
|
42
|
+
- [Observing Provider Stream Events](#observing-provider-stream-events)
|
|
40
43
|
- [Custom Providers](#custom-providers)
|
|
41
44
|
- [createProvider()](#createprovider)
|
|
42
45
|
- [Calling API Implementations Directly](#calling-api-implementations-directly)
|
|
43
46
|
- [OpenAI Compatibility Settings](#openai-compatibility-settings)
|
|
44
47
|
- [Faux Provider for Tests](#faux-provider-for-tests)
|
|
45
48
|
- [Cross-Provider Handoffs](#cross-provider-handoffs)
|
|
49
|
+
- [System Messages](#system-messages)
|
|
46
50
|
- [Context Serialization](#context-serialization)
|
|
47
51
|
- [Browser Usage](#browser-usage)
|
|
48
52
|
- [Bundling and Tree Shaking](#bundling-and-tree-shaking)
|
|
@@ -59,7 +63,9 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
59
63
|
- **OpenAI**
|
|
60
64
|
- **Ant Ling**
|
|
61
65
|
- **Azure OpenAI (Responses)**
|
|
62
|
-
- **OpenAI Codex** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
|
|
66
|
+
- **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
|
|
67
|
+
- **Radius** (API key or OAuth, with a dynamically refreshed gateway catalog)
|
|
68
|
+
- **TypeSafe** (System One classifier API)
|
|
63
69
|
- **DeepSeek**
|
|
64
70
|
- **NVIDIA NIM**
|
|
65
71
|
- **Anthropic**
|
|
@@ -76,6 +82,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
76
82
|
- **ZAI Coding Plan (Global)** (with separate China provider)
|
|
77
83
|
- **MiniMax** (with separate China provider)
|
|
78
84
|
- **Together AI**
|
|
85
|
+
- **Baseten**
|
|
79
86
|
- **Hugging Face**
|
|
80
87
|
- **Moonshot AI** (with separate China provider)
|
|
81
88
|
- **GitHub Copilot** (requires OAuth, see below)
|
|
@@ -84,6 +91,8 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
84
91
|
- **OpenCode Go**
|
|
85
92
|
- **Fireworks** (uses OpenAI- and Anthropic-compatible APIs)
|
|
86
93
|
- **Kimi For Coding** (Moonshot AI subscription endpoint, uses Anthropic-compatible API)
|
|
94
|
+
- **Meta** (Model API, uses OpenAI Responses-compatible API)
|
|
95
|
+
- **Qwen Token Plan** (separate Individual and existing catalogs, with a separate China provider)
|
|
87
96
|
- **Xiaomi MiMo** (defaults to API billing endpoint, with separate Token Plan providers for `cn`/`ams`/`sgp` regions)
|
|
88
97
|
- **Any OpenAI-compatible API**: Ollama, vLLM, LM Studio, etc.
|
|
89
98
|
|
|
@@ -269,7 +278,7 @@ Reads are synchronous and return the last-known lists:
|
|
|
269
278
|
const providers = models.getProviders(); // registered Provider objects
|
|
270
279
|
const provider = models.getProvider('anthropic'); // one provider
|
|
271
280
|
|
|
272
|
-
const all = models.getModels(); // every model across providers
|
|
281
|
+
const all = models.getModels(); // every chat model across providers
|
|
273
282
|
const anthropicModels = models.getModels('anthropic');
|
|
274
283
|
const model = models.getModel('anthropic', 'claude-sonnet-4-5');
|
|
275
284
|
|
|
@@ -282,7 +291,31 @@ for (const m of anthropicModels) {
|
|
|
282
291
|
}
|
|
283
292
|
```
|
|
284
293
|
|
|
285
|
-
|
|
294
|
+
The unqualified reads `getModels()`/`getModel()`/`getAvailable()` return chat models (`Model<Api>`) usable with `stream()`. The `*OfType` reads return one model type, and `getAllModels()`/`getAllAvailable()` return every type as `AnyModel`:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const images = models.getModelsOfType('image', 'openrouter'); // ImageModel[]
|
|
298
|
+
const flux = models.getModelOfType('image', 'openrouter', 'black-forest-labs/flux.2-pro');
|
|
299
|
+
const jev = models.getModelOfType('classifier', 'typesafe', 'jev-latest');
|
|
300
|
+
const availableImages = await models.getAvailableOfType('image');
|
|
301
|
+
const everything = models.getAllModels(); // AnyModel[]
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The model's `type` decides which operation accepts it: chat models stream, `type: "image"` models generate images, and `type: "classifier"` models classify structured state. `type` is optional on chat models, so a model without `type` is a chat model. Do not compare `type` directly; narrow mixed lists with `isModelType()` or read the effective type with `getModelType()`:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { isModelType } from '@earendil-works/pi-ai';
|
|
308
|
+
|
|
309
|
+
for (const model of models.getAllModels()) {
|
|
310
|
+
if (isModelType(model, 'image')) {
|
|
311
|
+
// model: ImageModel<ImageApi>
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
IDs are unique within each provider and type; one upstream model may have separate entries for different operations. On a provider, `getModels()` returns chat models and the optional `getAllModels()` returns every type; providers with only chat models can omit it.
|
|
317
|
+
|
|
318
|
+
Dynamically listed chat models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
|
|
286
319
|
|
|
287
320
|
```typescript
|
|
288
321
|
import { hasApi } from '@earendil-works/pi-ai';
|
|
@@ -299,11 +332,26 @@ if (m && hasApi(m, 'anthropic-messages')) {
|
|
|
299
332
|
For tooling that wants the generated built-in catalog with full literal typing (provider and model IDs auto-complete), independent of any collection:
|
|
300
333
|
|
|
301
334
|
```typescript
|
|
302
|
-
import {
|
|
335
|
+
import {
|
|
336
|
+
getAllBuiltinModels,
|
|
337
|
+
getBuiltinClassifierModel,
|
|
338
|
+
getBuiltinClassifierModels,
|
|
339
|
+
getBuiltinImageModel,
|
|
340
|
+
getBuiltinImageModels,
|
|
341
|
+
getBuiltinModel,
|
|
342
|
+
getBuiltinModels,
|
|
343
|
+
getBuiltinProviders,
|
|
344
|
+
} from '@earendil-works/pi-ai/providers/all';
|
|
303
345
|
|
|
304
346
|
const model = getBuiltinModel('openai', 'gpt-4o-mini'); // typed Model<'openai-responses'>
|
|
347
|
+
const radius = getBuiltinModel('radius', 'balanced'); // typed Model<'pi-messages'>
|
|
348
|
+
const flux = getBuiltinImageModel('openrouter', 'black-forest-labs/flux.2-pro');
|
|
349
|
+
const jev = getBuiltinClassifierModel('typesafe', 'jev-latest');
|
|
305
350
|
const providers = getBuiltinProviders();
|
|
306
|
-
const
|
|
351
|
+
const openrouterChat = getBuiltinModels('openrouter'); // Model[]
|
|
352
|
+
const openrouterImages = getBuiltinImageModels('openrouter'); // ImageModel[]
|
|
353
|
+
const typesafeClassifiers = getBuiltinClassifierModels('typesafe'); // ClassifierModel[]
|
|
354
|
+
const openrouterAll = getAllBuiltinModels('openrouter'); // AnyModel[]
|
|
307
355
|
```
|
|
308
356
|
|
|
309
357
|
### Dynamic Providers
|
|
@@ -312,12 +360,12 @@ Providers may have dynamic model lists (a llama.cpp server, a live OpenRouter li
|
|
|
312
360
|
|
|
313
361
|
```typescript
|
|
314
362
|
// getModels() returns the last-known list (empty before the first refresh)
|
|
315
|
-
await models.refresh('llamacpp');
|
|
316
|
-
await models.refresh();
|
|
363
|
+
await models.refresh({ providers: ['llamacpp'] }); // refresh one provider
|
|
364
|
+
await models.refresh(); // refresh all providers concurrently, best-effort
|
|
317
365
|
const fresh = models.getModel('llamacpp', 'qwen3-30b');
|
|
318
366
|
```
|
|
319
367
|
|
|
320
|
-
Static built-in providers are no-ops for `refresh()`. See [createProvider()](#createprovider) for building a dynamic provider.
|
|
368
|
+
Static built-in providers are no-ops for `refresh()`. Radius is both static and dynamic: it ships the public `radius.pi.dev` catalog for synchronous API lookup, then overlays cached and freshly fetched `/v1/config` models when refreshed with configured auth. See [createProvider()](#createprovider) for building a dynamic provider.
|
|
321
369
|
|
|
322
370
|
## Auth
|
|
323
371
|
|
|
@@ -351,6 +399,8 @@ if (modelAuth) {
|
|
|
351
399
|
|
|
352
400
|
Both overloads resolve credentials, refresh expired OAuth when necessary, and may return an auth-derived `apiKey`, `headers`, or `baseUrl`. `getAuth()` resolves `undefined` for unconfigured providers and rejects with `ModelsError` when something is actually broken (`"oauth"`: token refresh failed, credential preserved for re-login; `"auth"`: key resolution or credential store failure). Request paths surface the same failures as stream errors.
|
|
353
401
|
|
|
402
|
+
`getAuth()`, `checkAuth()`, `getAvailable()`, login, and logout accept optional caller cancellation through their existing options or interaction objects and remain unbounded when no signal is supplied. Provider `login`, `ApiKeyAuth.check`, `ApiKeyAuth.resolve`, and `OAuthAuth.refresh` implementations always receive a concrete signal and must honor it for blocking work.
|
|
403
|
+
|
|
354
404
|
### Transforming Request Headers
|
|
355
405
|
|
|
356
406
|
`Models.stream()`, `complete()`, `streamSimple()`, and `completeSimple()` accept a Models-only `transformHeaders` option. It runs once after provider auth, `model.headers`, and explicit `options.headers` have been merged, but before provider dispatch:
|
|
@@ -387,7 +437,7 @@ const models = createModels({ credentials: myFileBackedStore });
|
|
|
387
437
|
// const models = builtinModels({ credentials: myFileBackedStore });
|
|
388
438
|
```
|
|
389
439
|
|
|
390
|
-
The contract is small: `read(providerId)`, `list()` for non-secret `{ providerId, type }` metadata, `modify(providerId, fn)` (the only write path — a serialized read-modify-write), and `delete(providerId)`. Enumeration must not resolve secrets or execute configured key commands. OAuth token refresh runs inside `modify`, so concurrent requests and processes cannot double-refresh a rotated token. A stored credential *owns* its provider: environment variables are only consulted when nothing is stored, and a failed refresh never silently falls back to an env key.
|
|
440
|
+
The contract is small: `read(providerId)`, `list()` for non-secret `{ providerId, type }` metadata, `modify(providerId, fn)` (the only write path — a serialized read-modify-write), and `delete(providerId)`. Each operation accepts optional cancellation options. Enumeration must not resolve secrets or execute configured key commands. OAuth token refresh runs inside `modify`, so concurrent requests and processes cannot double-refresh a rotated token. A stored credential *owns* its provider: environment variables are only consulted when nothing is stored, and a failed refresh never silently falls back to an env key.
|
|
391
441
|
|
|
392
442
|
API-key credentials use the same discriminator as pi's `auth.json` and can carry provider-scoped env/config values:
|
|
393
443
|
|
|
@@ -412,6 +462,8 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
|
|
|
412
462
|
| Ant Ling | `ANT_LING_API_KEY` |
|
|
413
463
|
| Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` (e.g. `https://{resource}.ai.azure.com`) or `AZURE_OPENAI_RESOURCE_NAME`. Supports `*.openai.azure.com`, `*.cognitiveservices.azure.com` and `*.ai.azure.com`; root endpoints auto-normalize to `/openai/v1`. Optional: `AZURE_OPENAI_API_VERSION` (default `v1`), `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`. |
|
|
414
464
|
| Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
|
|
465
|
+
| Radius | `RADIUS_API_KEY` |
|
|
466
|
+
| TypeSafe | `TYPESAFE_API_KEY` |
|
|
415
467
|
| DeepSeek | `DEEPSEEK_API_KEY` |
|
|
416
468
|
| NVIDIA NIM | `NVIDIA_API_KEY` |
|
|
417
469
|
| Google | `GEMINI_API_KEY` |
|
|
@@ -424,6 +476,7 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
|
|
|
424
476
|
| xAI | `XAI_API_KEY` |
|
|
425
477
|
| Fireworks | `FIREWORKS_API_KEY` |
|
|
426
478
|
| Together AI | `TOGETHER_API_KEY` |
|
|
479
|
+
| Baseten | `BASETEN_API_KEY` |
|
|
427
480
|
| OpenRouter | `OPENROUTER_API_KEY` |
|
|
428
481
|
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
|
|
429
482
|
| ZAI Coding Plan (Global) | `ZAI_API_KEY` |
|
|
@@ -434,7 +487,9 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
|
|
|
434
487
|
| Hugging Face | `HF_TOKEN` |
|
|
435
488
|
| OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
|
|
436
489
|
| Kimi For Coding | `KIMI_API_KEY` |
|
|
437
|
-
|
|
|
490
|
+
| Meta | `META_API_KEY` |
|
|
491
|
+
| Qwen Token Plan (existing catalog) | `QWEN_TOKEN_PLAN_API_KEY` |
|
|
492
|
+
| Qwen Token Plan (Individual) | `QWEN_TOKEN_PLAN_API_KEY` |
|
|
438
493
|
| Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` |
|
|
439
494
|
| Xiaomi MiMo (API billing) | `XIAOMI_API_KEY` |
|
|
440
495
|
| Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` |
|
|
@@ -442,6 +497,11 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
|
|
|
442
497
|
| Xiaomi MiMo Token Plan (Singapore) | `XIAOMI_TOKEN_PLAN_SGP_API_KEY` |
|
|
443
498
|
| GitHub Copilot | `COPILOT_GITHUB_TOKEN` |
|
|
444
499
|
|
|
500
|
+
`qwen-token-plan-individual` and `qwen-token-plan` share the international endpoint and
|
|
501
|
+
`QWEN_TOKEN_PLAN_API_KEY`. The Individual provider exposes only the models documented for Individual
|
|
502
|
+
subscriptions, while the existing provider retains its broader catalog for backward compatibility.
|
|
503
|
+
Stored credentials remain provider-scoped, so save the key under the provider ID you register.
|
|
504
|
+
|
|
445
505
|
Amazon Bedrock resolves ambient AWS credentials (`AWS_PROFILE`, access key pairs, `AWS_BEARER_TOKEN_BEDROCK`, ECS task roles, web identity tokens); its provider-owned login flow supports bearer tokens, AWS profiles, and the existing credential chain. Vertex AI resolves either an explicit key or gcloud Application Default Credentials plus project/location, with a provider-owned login flow for API keys, ADC, and service-account files.
|
|
446
506
|
|
|
447
507
|
## Tools
|
|
@@ -640,6 +700,10 @@ for await (const event of s) {
|
|
|
640
700
|
|
|
641
701
|
### Complete Event Reference
|
|
642
702
|
|
|
703
|
+
Successful generation follows `start → updates* → done`. A failure after generation starts follows `start → updates* → error`. Request setup may fail before generation starts, in which case the stream contains only `error`; `done` and update events are invalid before `start`. Direct API `streamSimple()` calls throw synchronously when request auth is missing.
|
|
704
|
+
|
|
705
|
+
Every non-terminal event's `partial` is the shared live response-so-far helper. It is intentionally not an event-time snapshot: providers may mutate the same message and content blocks as generation advances, including while older events wait in the stream queue. Inspect it when handling an event instead of retaining it as historical state. Text and ordinary thinking blocks are empty when their `*_start` event is emitted and grow only through matching `*_delta` events until the authoritative `*_end`; redacted thinking may be complete at start and emit no deltas. Tool-call arguments at `toolcall_start` are provider-specific; `toolcall_delta` carries subsequent JSON updates.
|
|
706
|
+
|
|
643
707
|
All streaming events emitted during assistant message generation:
|
|
644
708
|
|
|
645
709
|
| Event Type | Description | Key Properties |
|
|
@@ -653,12 +717,40 @@ All streaming events emitted during assistant message generation:
|
|
|
653
717
|
| `thinking_end` | Thinking block complete | `content`: Full thinking, `contentIndex`: Position |
|
|
654
718
|
| `toolcall_start` | Tool call begins | `contentIndex`: Position in content array |
|
|
655
719
|
| `toolcall_delta` | Tool arguments streaming | `delta`: JSON chunk, `partial.content[contentIndex].arguments`: Partial parsed args |
|
|
656
|
-
| `toolcall_end` | Tool call complete | `toolCall`: Complete validated tool call with `id`, `name`, `arguments` |
|
|
720
|
+
| `toolcall_end` | Tool call complete | `toolCall`: Complete, but not schema-validated, tool call with `id`, `name`, `arguments` |
|
|
657
721
|
| `done` | Stream complete | `reason`: Stop reason ("stop", "length", "toolUse"), `message`: Final assistant message |
|
|
658
722
|
| `error` | Error occurred | `reason`: Error type ("error" or "aborted"), `error`: AssistantMessage with partial content |
|
|
659
723
|
|
|
660
724
|
Streaming events for different content blocks are not guaranteed to be contiguous. Providers may emit deltas for text, thinking, and tool calls in the same upstream chunk, and pi may surface corresponding events interleaved, for example `text_start`, `text_delta`, `toolcall_start`, `text_delta`, `toolcall_delta`. Consumers must use `contentIndex` to associate each delta/end event with its block and must not assume that a block's `*_start`/`*_delta`/`*_end` sequence is uninterrupted by events for other blocks.
|
|
661
725
|
|
|
726
|
+
### Compact Assistant Message Frames
|
|
727
|
+
|
|
728
|
+
`AssistantMessageFrameEncoder` converts one stream into compact, persistable `AssistantMessageFrame` values. Create one encoder per stream and feed it every event in order. The encoder understands that `partial` is live: a block-start event consumed after the provider has already queued later deltas snapshots the current block once, and covered queued text/thinking deltas produce no duplicate frame. It retains only per-open-block counters plus, temporarily, the raw prefix needed to synchronize an already-advanced tool call. It never clones the growing full partial per token.
|
|
729
|
+
|
|
730
|
+
The start frame contains message metadata with empty content. Text and thinking frames store each generated character at most once before the authoritative end frame. Tool calls that were already advanced when their start event was consumed use one compact JSON checkpoint before ordinary deltas resume. Terminal `done` and `error` events produce no frame because final message settlement is separate. A pre-generation `error` therefore produces no frames.
|
|
731
|
+
|
|
732
|
+
`reduceAssistantMessageFrames()` is the canonical pure reducer. It reconstructs text, thinking, and tool-call arguments, including interleaved blocks identified by `contentIndex`, and rejects malformed sequences. It performs a single pass over the iterable and returns `undefined` when there is no start frame. End frames replace blocks with the provider's authoritative completed content and metadata. The reducer does not validate tool arguments against a TypeBox schema; call `validateToolCall` before execution.
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
import {
|
|
736
|
+
AssistantMessageFrameEncoder,
|
|
737
|
+
reduceAssistantMessageFrames,
|
|
738
|
+
type AssistantMessageFrame,
|
|
739
|
+
} from '@earendil-works/pi-ai';
|
|
740
|
+
|
|
741
|
+
const encoder = new AssistantMessageFrameEncoder();
|
|
742
|
+
const frames: AssistantMessageFrame[] = [];
|
|
743
|
+
for await (const event of s) {
|
|
744
|
+
const frame = encoder.encode(event);
|
|
745
|
+
if (frame) frames.push(frame);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const reconstructedPartial = reduceAssistantMessageFrames(frames);
|
|
749
|
+
const finalMessage = await s.result(); // Persist terminal settlement separately.
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
An encoder rejects duplicate starts, updates before start, `done` before start, events after a terminal event, duplicate block starts, and block-kind mismatches. An `error` before start is valid and returns no frame.
|
|
753
|
+
|
|
662
754
|
## Image Input
|
|
663
755
|
|
|
664
756
|
Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
|
|
@@ -697,20 +789,19 @@ for (const block of response.content) {
|
|
|
697
789
|
|
|
698
790
|
## Image Generation
|
|
699
791
|
|
|
700
|
-
Image
|
|
792
|
+
Image models live in the same `Models` collection and on the same `Provider` as chat models, so one credential per provider covers both. They are typed `ImageModel` with `type: "image"` and are used through `generateImages()`, a one-shot API that waits for the provider response and returns the final `AssistantImages` result. Do not use the chat/stream APIs for them; `stream()` rejects image models.
|
|
701
793
|
|
|
702
794
|
### Basic Image Generation
|
|
703
795
|
|
|
704
796
|
```typescript
|
|
705
|
-
import {
|
|
797
|
+
import { builtinModels } from '@earendil-works/pi-ai/providers/all';
|
|
706
798
|
|
|
707
|
-
|
|
708
|
-
const imagesModels = builtinImagesModels();
|
|
799
|
+
const models = builtinModels();
|
|
709
800
|
|
|
710
|
-
const model =
|
|
801
|
+
const model = models.getModelOfType('image', 'openrouter', 'google/gemini-2.5-flash-image')!;
|
|
711
802
|
|
|
712
803
|
// Auth resolves through the provider (OPENROUTER_API_KEY here); explicit apiKey wins
|
|
713
|
-
const result = await
|
|
804
|
+
const result = await models.generateImages(model, {
|
|
714
805
|
input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
|
|
715
806
|
});
|
|
716
807
|
|
|
@@ -724,7 +815,33 @@ for (const block of result.output) {
|
|
|
724
815
|
}
|
|
725
816
|
```
|
|
726
817
|
|
|
727
|
-
|
|
818
|
+
`generateImages()` accepts only `ImageModel` values. If an upstream model supports both chat and image generation, the catalog contains separate entries with the same provider and ID: `getModel()` returns its chat operation and `getModelOfType('image', ...)` returns its image operation. Failures never reject; they return an `AssistantImages` with `stopReason: "error"`, including unknown providers, unconfigured auth, and providers without an image implementation.
|
|
819
|
+
|
|
820
|
+
A provider declares image support with the `images` option of [`createProvider()`](#createprovider): a map from `model.api` to an implementation with `generateImages()`. Image models go into the same `models` list as chat models. `api` becomes optional when `images` is present, so an image-only provider is just a provider without chat models:
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
import { createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
|
|
824
|
+
|
|
825
|
+
const pixels = createProvider({
|
|
826
|
+
id: 'pixels',
|
|
827
|
+
auth: { apiKey: envApiKeyAuth('Pixels API key', ['PIXELS_API_KEY']) },
|
|
828
|
+
models: [{
|
|
829
|
+
type: 'image',
|
|
830
|
+
id: 'flux-pro',
|
|
831
|
+
name: 'FLUX Pro',
|
|
832
|
+
api: 'pixels-images',
|
|
833
|
+
provider: 'pixels',
|
|
834
|
+
baseUrl: 'https://api.pixels.test/v1',
|
|
835
|
+
input: ['text'],
|
|
836
|
+
output: ['image'],
|
|
837
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
838
|
+
}],
|
|
839
|
+
images: {
|
|
840
|
+
'pixels-images': { generateImages: async (model, context, options) => { /* ... */ } },
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
models.setProvider(pixels);
|
|
844
|
+
```
|
|
728
845
|
|
|
729
846
|
The old global API (`getImageModel()` / `getImageModels()` / `getImageProviders()` / `generateImages()`) remains available on the [compat entrypoint](#migrating-from-the-old-global-api):
|
|
730
847
|
|
|
@@ -745,7 +862,7 @@ Some models also support image input:
|
|
|
745
862
|
import { readFileSync } from 'fs';
|
|
746
863
|
|
|
747
864
|
const imageBuffer = readFileSync('input.png');
|
|
748
|
-
const result = await
|
|
865
|
+
const result = await models.generateImages(model, {
|
|
749
866
|
input: [
|
|
750
867
|
{ type: 'text', text: 'Create a variation of this image with a blue background.' },
|
|
751
868
|
{ type: 'image', data: imageBuffer.toString('base64'), mimeType: 'image/png' }
|
|
@@ -756,14 +873,13 @@ const result = await imagesModels.generateImages(model, {
|
|
|
756
873
|
Check capabilities on the model metadata:
|
|
757
874
|
|
|
758
875
|
```typescript
|
|
759
|
-
console.log(model.input);
|
|
760
|
-
console.log(model.output);
|
|
876
|
+
console.log(model.input); // ['text'] or ['text', 'image']
|
|
877
|
+
console.log(model.output); // ['image'] or ['image', 'text']
|
|
761
878
|
```
|
|
762
879
|
|
|
763
880
|
### Notes and Limitations
|
|
764
881
|
|
|
765
|
-
- Image models
|
|
766
|
-
- Use `generateImages()`, not the chat/stream APIs.
|
|
882
|
+
- Image models and chat models share `Models` and `Provider`; list them with `getModelsOfType('image')` and run them with `generateImages()`, never the chat/stream APIs.
|
|
767
883
|
- Image-generation models do not participate in tool calling.
|
|
768
884
|
- Outputs are returned in `AssistantImages.output` and can include both base64-encoded `ImageContent` blocks and `TextContent` blocks.
|
|
769
885
|
- Some models return only images, others return images plus text. Check `model.output`.
|
|
@@ -772,6 +888,97 @@ console.log(model.output); // ['image'] or ['image', 'text']
|
|
|
772
888
|
- If you want a model to analyze images in a conversation or call tools, use the regular chat APIs with a model that supports image input.
|
|
773
889
|
- At the moment, image generation is available through only one provider, OpenRouter.
|
|
774
890
|
|
|
891
|
+
## Classification
|
|
892
|
+
|
|
893
|
+
Classifier models consume structured JSON state and answer one or more typed questions. They do not use chat or image-generation APIs. TypeSafe's Jev model is available from these built-in providers:
|
|
894
|
+
|
|
895
|
+
| Provider | Model IDs | Auth |
|
|
896
|
+
| --- | --- | --- |
|
|
897
|
+
| `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
|
|
898
|
+
| `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or OpenRouter OAuth |
|
|
899
|
+
| `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
|
|
900
|
+
| `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
|
|
901
|
+
| `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
|
|
902
|
+
|
|
903
|
+
```typescript
|
|
904
|
+
import { builtinModels } from '@earendil-works/pi-ai/providers/all';
|
|
905
|
+
|
|
906
|
+
const models = builtinModels();
|
|
907
|
+
const model = models.getModelOfType('classifier', 'typesafe', 'jev-latest')!;
|
|
908
|
+
const result = await models.classify(model, {
|
|
909
|
+
state: { message: 'The change works perfectly, thanks.' },
|
|
910
|
+
questions: {
|
|
911
|
+
category: {
|
|
912
|
+
type: 'choice',
|
|
913
|
+
instructions: 'Classify the message.',
|
|
914
|
+
criteria: {
|
|
915
|
+
approval: 'The user approves of the result',
|
|
916
|
+
correction: 'The user requests a correction'
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
satisfaction: {
|
|
920
|
+
type: 'score',
|
|
921
|
+
instructions: 'Score user satisfaction.',
|
|
922
|
+
criteria: ['dissatisfied', 'neutral', 'satisfied']
|
|
923
|
+
},
|
|
924
|
+
approved: {
|
|
925
|
+
type: 'bool',
|
|
926
|
+
instructions: 'Does the user approve?',
|
|
927
|
+
criteria: { true: 'Approval', false: 'No approval' }
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
console.log(result.answers);
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
The public contract uses `bool` questions and `{ type: "bool", probability }` answers. The TypeSafe adapter translates those to and from its `noul` wire representation. Like image generation, `classify()` resolves to a result with `stopReason: "error"` instead of rejecting for provider, authentication, or response errors.
|
|
936
|
+
|
|
937
|
+
When the service reports token counts, `result.usage` carries them with their cost at the model's catalog price, the same `Usage` shape as chat messages. All System One services report token counts; a request that was answered with malformed answers keeps its usage. Local classifiers such as `llama-cpp-classify` report no usage.
|
|
938
|
+
|
|
939
|
+
`ClassifierOptions.temperature` divides the answer logits by the given value before they are normalized; values above 1 soften the distribution. APIs that cannot apply it, such as System One, ignore it.
|
|
940
|
+
|
|
941
|
+
### Chat models on llama.cpp
|
|
942
|
+
|
|
943
|
+
The `llama-cpp-classify` API turns a chat model served by llama.cpp's `llama-server` into a classifier. Each question becomes one chat prompt: the state, every question of the request, the state again, and the question with its answers under single-token labels (letters for a choice, `Yes`/`No` for a bool, digits for a score). The prompt up to the final question is shared by all questions of a request, so the server's prompt cache evaluates the state once per request. The server returns the log-probabilities of the next token, and the answer is the softmax over the label tokens. Choices support up to 62 options and scores up to 10 levels. The model's `baseUrl` is the server URL; a trailing `/v1` is ignored. In router mode, the model ID selects the model.
|
|
944
|
+
|
|
945
|
+
```typescript
|
|
946
|
+
import { createProvider } from '@earendil-works/pi-ai';
|
|
947
|
+
import { llamaCppClassifyApi } from '@earendil-works/pi-ai/api/llama-cpp-classify.lazy';
|
|
948
|
+
|
|
949
|
+
const provider = createProvider({
|
|
950
|
+
id: 'local-llama',
|
|
951
|
+
auth: { apiKey: { name: 'llama.cpp', resolve: async () => ({ auth: {} }) } },
|
|
952
|
+
models: [{
|
|
953
|
+
type: 'classifier',
|
|
954
|
+
id: 'qwen3-4b',
|
|
955
|
+
name: 'Qwen3 4B',
|
|
956
|
+
api: 'llama-cpp-classify',
|
|
957
|
+
provider: 'local-llama',
|
|
958
|
+
baseUrl: 'http://127.0.0.1:8080',
|
|
959
|
+
input: ['text'],
|
|
960
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
961
|
+
contextWindow: 32768
|
|
962
|
+
}],
|
|
963
|
+
classifiers: { 'llama-cpp-classify': llamaCppClassifyApi() }
|
|
964
|
+
});
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
Raw label probabilities are usually overconfident; pass `temperature` above 1 to soften them.
|
|
968
|
+
|
|
969
|
+
Custom providers register classifier models and implementations by API ID:
|
|
970
|
+
|
|
971
|
+
```typescript
|
|
972
|
+
createProvider({
|
|
973
|
+
id: 'classifier-service',
|
|
974
|
+
auth,
|
|
975
|
+
models: [model],
|
|
976
|
+
classifiers: {
|
|
977
|
+
'classifier-api': { classify: async (model, context, options) => result }
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
```
|
|
981
|
+
|
|
775
982
|
## Thinking/Reasoning
|
|
776
983
|
|
|
777
984
|
Many models support thinking/reasoning capabilities where they can show their internal thought process. You can check if a model supports reasoning via the `reasoning` property. If you pass reasoning options to a non-reasoning model, they are silently ignored.
|
|
@@ -783,7 +990,7 @@ Many models support thinking/reasoning capabilities where they can show their in
|
|
|
783
990
|
const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
|
|
784
991
|
// or models.getModel('openai', 'gpt-5-mini');
|
|
785
992
|
// or models.getModel('google', 'gemini-2.5-flash');
|
|
786
|
-
// or models.getModel('xai', 'grok-4.
|
|
993
|
+
// or models.getModel('xai', 'grok-4.7');
|
|
787
994
|
|
|
788
995
|
// Check if model supports reasoning
|
|
789
996
|
if (model.reasoning) {
|
|
@@ -883,7 +1090,7 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
|
|
|
883
1090
|
|
|
884
1091
|
## Error Handling
|
|
885
1092
|
|
|
886
|
-
Request failures
|
|
1093
|
+
Request failures after a stream is returned never throw: when a request ends with an error (including aborts and tool call validation errors), the streaming API emits an error event and the final message carries the details. Setup failures may emit `error` without `start`; failures after generation begins emit `start`, any observed updates, then `error`. Direct API `streamSimple()` calls throw synchronously when request auth is missing:
|
|
887
1094
|
|
|
888
1095
|
```typescript
|
|
889
1096
|
// In streaming
|
|
@@ -905,7 +1112,7 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
|
|
|
905
1112
|
}
|
|
906
1113
|
```
|
|
907
1114
|
|
|
908
|
-
|
|
1115
|
+
When using a provider collection, auth failures (OAuth refresh failed, unknown provider) surface as a stream error with `stopReason: "error"`. Direct API `streamSimple()` calls instead throw synchronously when their required auth is absent.
|
|
909
1116
|
|
|
910
1117
|
### Aborting Requests
|
|
911
1118
|
|
|
@@ -980,11 +1187,30 @@ const response = await models.complete(model, context, {
|
|
|
980
1187
|
|
|
981
1188
|
The callback is supported by `stream`, `complete`, `streamSimple`, and `completeSimple`.
|
|
982
1189
|
|
|
1190
|
+
### Observing Provider Stream Events
|
|
1191
|
+
|
|
1192
|
+
Use `onProviderStreamEvent` to inspect provider-specific fields that Pi does not include in `AssistantMessage`. The callback receives the parsed event available to the adapter before Pi normalizes it. Treat the event as read-only because mutations can affect normalization. This is not guaranteed to be the original HTTP bytes or SSE frame.
|
|
1193
|
+
|
|
1194
|
+
```typescript
|
|
1195
|
+
const openRouterModel = models.getModel('openrouter', 'openrouter/auto')!;
|
|
1196
|
+
const response = await models.complete(openRouterModel, context, {
|
|
1197
|
+
headers: { "X-OpenRouter-Metadata": "enabled" },
|
|
1198
|
+
onProviderStreamEvent: (data) => {
|
|
1199
|
+
const chunk = data as Record<string, unknown>;
|
|
1200
|
+
if (chunk.openrouter_metadata) {
|
|
1201
|
+
console.log(chunk.openrouter_metadata);
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
});
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
Callbacks are awaited in stream order, so slow callbacks delay stream consumption and thrown errors fail the request. SDK-backed adapters can expose only fields retained by their SDK.
|
|
1208
|
+
|
|
983
1209
|
## Custom Providers
|
|
984
1210
|
|
|
985
1211
|
### createProvider()
|
|
986
1212
|
|
|
987
|
-
`createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation. Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
|
|
1213
|
+
`createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation (`api` for chat models, `images` for image generation, `classifiers` for classification; at least one is required, see [Image Generation](#image-generation)). Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
|
|
988
1214
|
|
|
989
1215
|
```typescript
|
|
990
1216
|
import { createModels, createProvider, envApiKeyAuth, type Model } from '@earendil-works/pi-ai';
|
|
@@ -1066,7 +1292,7 @@ const tenantGateway = createProvider({
|
|
|
1066
1292
|
});
|
|
1067
1293
|
```
|
|
1068
1294
|
|
|
1069
|
-
Dynamic model lists use `fetchModels
|
|
1295
|
+
Dynamic model lists use `fetchModels`, which can return models of every type. `Models.refresh()` refreshes every configured dynamic provider, passing its effective API-key or refreshed OAuth credential. A `ModelsStore` persists dynamic catalogs; both stores default to in-memory implementations. Its `read`, `write`, and `delete` operations accept optional cancellation, and `Models` binds those waits to the provider refresh signal.
|
|
1070
1296
|
|
|
1071
1297
|
```typescript
|
|
1072
1298
|
const models = createModels({ credentials, modelsStore });
|
|
@@ -1084,7 +1310,11 @@ if (result.aborted) console.log('refresh cancelled');
|
|
|
1084
1310
|
for (const [provider, error] of result.errors) console.error(provider, error);
|
|
1085
1311
|
```
|
|
1086
1312
|
|
|
1087
|
-
|
|
1313
|
+
`Models.refresh()` is unbounded when its optional signal is omitted. Providers always receive a concrete `RefreshModelsContext.signal` and must honor it for network requests and other blocking work. When a caller supplies a signal, `Models.refresh()` returns promptly with `aborted: true` after cancellation even if a custom provider fails to cooperate; the provider must still honor the signal to stop its underlying work.
|
|
1314
|
+
|
|
1315
|
+
Use `models.refresh({ providers: ['openrouter'] })` to restrict work to selected providers, `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access, or `models.refresh({ force: true })` to bypass provider freshness checks. Model reads stay synchronous and return the last restored or refreshed list.
|
|
1316
|
+
|
|
1317
|
+
`createProvider()` handles dynamic publication and persistence automatically. Handwritten `Provider.refreshModels()` implementations receive the read-only `context.stored` snapshot and publish through `context.publish({ persist?, update? })`. Omit `persist` to leave storage unchanged, pass a `ModelsStoreEntry` to write it, or pass `persist: null` to delete it. `ModelsStoreEntry.models` contains models of every type. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
|
|
1088
1318
|
|
|
1089
1319
|
Custom models can carry `headers` (e.g. proxies behind bot detection) and `compat` flags. `Models.getAuth(model)` includes those model headers, and stream methods merge them before explicit request headers and `transformHeaders`. See [OpenAI Compatibility Settings](#openai-compatibility-settings).
|
|
1090
1320
|
|
|
@@ -1120,12 +1350,13 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
|
|
|
1120
1350
|
|
|
1121
1351
|
### Calling API Implementations Directly
|
|
1122
1352
|
|
|
1123
|
-
The API implementations are importable on their own. Each module exports exactly `stream` and `streamSimple` with that API's full option typing. Direct calls bypass provider auth — pass `apiKey` explicitly
|
|
1353
|
+
The API implementations are importable on their own. Each module exports exactly `stream` and `streamSimple` with that API's full option typing. Direct calls bypass provider auth and context normalization — pass `apiKey` explicitly and wrap the context in `normalizeContext()`:
|
|
1124
1354
|
|
|
1125
1355
|
```typescript
|
|
1356
|
+
import { normalizeContext } from '@earendil-works/pi-ai';
|
|
1126
1357
|
import { stream } from '@earendil-works/pi-ai/api/anthropic-messages';
|
|
1127
1358
|
|
|
1128
|
-
const s = stream(claudeModel, context, {
|
|
1359
|
+
const s = stream(claudeModel, normalizeContext(context), {
|
|
1129
1360
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
1130
1361
|
thinkingEnabled: true,
|
|
1131
1362
|
thinkingBudgetTokens: 2048,
|
|
@@ -1158,17 +1389,22 @@ interface OpenAICompletionsCompat {
|
|
|
1158
1389
|
supportsDeveloperRole?: boolean; // Whether provider supports `developer` role vs `system` (default: true)
|
|
1159
1390
|
supportsReasoningEffort?: boolean; // Whether provider supports `reasoning_effort` (default: true)
|
|
1160
1391
|
supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
|
|
1161
|
-
supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default:
|
|
1392
|
+
supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: false; enabled in metadata for capable built-in models)
|
|
1162
1393
|
supportsOpenAIGrammarTools?: boolean; // Whether to emit OpenAI custom Lark/regex grammar tools; false falls back to normal function tools (default: false; the generated catalog enables it for capable models)
|
|
1163
|
-
|
|
1394
|
+
supportsMidConvoSystemMessages?: boolean; // Whether the model accepts system messages after the conversation started; false folds them into the leading prompt (default: false; the generated catalog enables it for verified models)
|
|
1395
|
+
supportsMidConvoToolAdditions?: boolean; // Whether system messages can add tools mid-conversation via Kimi-style `tools` system messages; requires supportsMidConvoSystemMessages (default: false)
|
|
1396
|
+
sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: true for OpenRouter, false otherwise)
|
|
1164
1397
|
sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Format for session affinity: 'openai' uses `prompt_cache_key`, `session_id`, `x-client-request-id`, and `x-session-affinity`; 'openai-nosession' uses `prompt_cache_key`, `x-client-request-id`, and `x-session-affinity`; 'openrouter' uses `x-session-id` (default: auto-detected)
|
|
1165
1398
|
maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
|
|
1166
1399
|
requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
|
|
1167
1400
|
requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
|
|
1168
1401
|
requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false)
|
|
1169
1402
|
requiresReasoningContentOnAssistantMessages?: boolean; // Whether all replayed assistant messages must include empty reasoning_content when reasoning is enabled (default: auto-detected for DeepSeek)
|
|
1170
|
-
thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking' | 'ant-ling'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking, 'ant-ling' uses reasoning: { effort } only for mapped efforts (default: openai)
|
|
1171
|
-
chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for pi-controlled thinking values
|
|
1403
|
+
thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'baseten' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking' | 'ant-ling'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'baseten' uses configurable chat_template_args plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking, 'ant-ling' uses reasoning: { effort } only for mapped efforts (default: openai)
|
|
1404
|
+
chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort' | 'thinking.budget'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for pi-controlled thinking values
|
|
1405
|
+
chatTemplateArgs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort' | 'thinking.budget'; omitWhenOff?: boolean }>; // chat_template_args values for thinkingFormat: 'baseten'; use $var for pi-controlled thinking values
|
|
1406
|
+
thinkingTokenBudgetField?: 'thinking_token_budget' | 'thinking_budget' | 'thinking_budget_tokens'; // Top-level field that caps reasoning tokens from thinkingBudgets (vLLM / Qwen / llama.cpp). Off by default.
|
|
1407
|
+
supportsThinkingTokenBudget?: boolean; // Alias for thinkingTokenBudgetField: 'thinking_token_budget' (vLLM). Prefer thinkingTokenBudgetField. Default: false.
|
|
1172
1408
|
cacheControlFormat?: 'anthropic'; // Anthropic-style cache_control on system prompt, last tool, and last user/assistant text content
|
|
1173
1409
|
openRouterRouting?: OpenRouterRouting; // OpenRouter routing preferences (default: {})
|
|
1174
1410
|
vercelGatewayRouting?: VercelGatewayRouting; // Vercel AI Gateway routing preferences (default: {})
|
|
@@ -1183,6 +1419,8 @@ interface OpenAIResponsesCompat {
|
|
|
1183
1419
|
}
|
|
1184
1420
|
```
|
|
1185
1421
|
|
|
1422
|
+
OpenRouter requests send `x-session-id` from `sessionId` when prompt caching is enabled. Chat Completions and Anthropic Messages both auto-detect OpenRouter endpoints unless `sendSessionAffinityHeaders` is explicitly false. On Anthropic-compatible models, `sessionAffinityFormat: "openrouter"` selects `x-session-id`; when unset, the existing `x-session-affinity` format is used. Explicit request headers take precedence over generated headers.
|
|
1423
|
+
|
|
1186
1424
|
If `compat` is not set, the library falls back to URL-based detection. If `compat` is partially set, unspecified fields use the detected defaults. This is useful for:
|
|
1187
1425
|
|
|
1188
1426
|
- **LiteLLM proxies**: May not support `store` field
|
|
@@ -1318,6 +1556,39 @@ const geminiResponse = await models.complete(gemini, context);
|
|
|
1318
1556
|
|
|
1319
1557
|
All providers can handle messages from other providers — text, tool calls and results (including images), thinking blocks (transformed to tagged text), and aborted messages with partial content. This enables flexible workflows: start with a fast model, switch to a more capable one for complex reasoning, or maintain continuity across provider outages.
|
|
1320
1558
|
|
|
1559
|
+
## System Messages
|
|
1560
|
+
|
|
1561
|
+
`Context.systemPrompt` and `Context.tools` are shorthand for a leading system message. The public entry points (`Models.stream()`, `streamSimple()`, `complete()`, `completeSimple()`) accept a `Context` and call `normalizeContext()` once; everything below them, including `Provider.stream()`, `ProviderStreams`, and the API implementation modules, receives the resulting `TranscriptContext`, which only has `messages`. The transcript can also carry system messages later in the conversation to change the prompt or the tool set without rewriting the history:
|
|
1562
|
+
|
|
1563
|
+
```typescript
|
|
1564
|
+
interface SystemMessage {
|
|
1565
|
+
role: "system";
|
|
1566
|
+
content: string | TextContent[]; // leading: base prompt; later: added instructions
|
|
1567
|
+
sections?: Record<string, string | null>; // named prompt sections; later messages patch by name, null removes
|
|
1568
|
+
toolsAdded?: Tool[]; // tools that become available here
|
|
1569
|
+
toolsRemoved?: ToolReference[]; // tools that stop being available here
|
|
1570
|
+
timestamp: number;
|
|
1571
|
+
}
|
|
1572
|
+
```
|
|
1573
|
+
|
|
1574
|
+
Sections are opaque text rendered verbatim after `content`, joined by blank lines. Keep each one self-delimiting (a tag, a heading) so the model can relate an update to the original. Replaying every system message in order yields the current prompt and tools; the replay helpers take the message list:
|
|
1575
|
+
|
|
1576
|
+
```typescript
|
|
1577
|
+
import { getCurrentSystemPrompt, getCurrentTools } from "@earendil-works/pi-ai";
|
|
1578
|
+
|
|
1579
|
+
const messages: Message[] = [
|
|
1580
|
+
{ role: "system", content: "You are helpful.", sections: { rules: "<rules>Be brief.</rules>" }, toolsAdded: [readTool], timestamp: 1 },
|
|
1581
|
+
{ role: "user", content: "hi", timestamp: 2 },
|
|
1582
|
+
{ role: "system", content: "", sections: { rules: "<rules>Be thorough.</rules>" }, toolsRemoved: [{ name: "read" }], timestamp: 3 },
|
|
1583
|
+
];
|
|
1584
|
+
getCurrentSystemPrompt(messages); // "You are helpful.\n\n<rules>Be thorough.</rules>"
|
|
1585
|
+
getCurrentTools(messages); // []
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
A custom `Provider` or `ProviderStreams` implementation reads the prompt and tools the same way from `context.messages`; `context.systemPrompt` and `context.tools` do not exist at that layer.
|
|
1589
|
+
|
|
1590
|
+
Models that accept system messages mid-conversation (`supportsMidConvoSystemMessages` in the model's compat settings, set by the generated catalog for verified models) receive each later system message in place, so the cached prefix stays intact; section changes are framed by name for the model. Every other model receives `collapseSystemMessages(transcript)`: the replayed prompt and current tools as the leading system message, with later system messages dropped. Anthropic models that also set `supportsMidConvoToolChanges` send tool changes as native blocks (`inline-tools-2026-09-15` beta): the top-level tool list holds the initial tools plus a stable deferred placeholder and never changes, later tools are defined by value in `tool_addition` blocks (a new definition under an existing name replaces the old one), and removals are `tool_removal` references, so tool changes do not invalidate the prompt cache. That needs at least one initial tool; otherwise the current tool list is sent at the top level with the system text only. OpenAI Responses models with `supportsAdditionalTools` or `supportsToolSearch` anchor additive tool changes at their message; everything else sends the current tool list at the top level.
|
|
1591
|
+
|
|
1321
1592
|
## Context Serialization
|
|
1322
1593
|
|
|
1323
1594
|
The `Context` object can be easily serialized and deserialized using standard JSON methods, making it simple to persist conversations, implement chat history, or transfer contexts between services:
|
|
@@ -1382,10 +1653,10 @@ Browser compatibility notes:
|
|
|
1382
1653
|
|
|
1383
1654
|
## Bundling and Tree Shaking
|
|
1384
1655
|
|
|
1385
|
-
For small bundles, import only the providers you need:
|
|
1656
|
+
For small bundles and low-overhead unbundled scripts, import the model runtime and only the providers you need:
|
|
1386
1657
|
|
|
1387
1658
|
```typescript
|
|
1388
|
-
import { createModels } from '@earendil-works/pi-ai';
|
|
1659
|
+
import { createModels } from '@earendil-works/pi-ai/models';
|
|
1389
1660
|
import { openaiProvider } from '@earendil-works/pi-ai/providers/openai';
|
|
1390
1661
|
|
|
1391
1662
|
const models = createModels();
|
|
@@ -1394,7 +1665,8 @@ models.setProvider(openaiProvider());
|
|
|
1394
1665
|
|
|
1395
1666
|
Rules:
|
|
1396
1667
|
|
|
1397
|
-
- `@earendil-works/pi-ai`
|
|
1668
|
+
- `@earendil-works/pi-ai/models` exports the model runtime (`createModels`, `createProvider`, model helpers, and their types) without TypeBox, built-in catalogs, or SDK implementations. Other types can still use `import type` from the root.
|
|
1669
|
+
- `@earendil-works/pi-ai` is the core entrypoint and does not import built-in catalogs, real provider factories, or SDK implementations, but it eagerly imports TypeBox and schema validation. Unbundled Node scripts do not tree-shake its unused exports; prefer `./models`, `./providers/faux`, and specific `./utils/*` subpaths when those are all you need.
|
|
1398
1670
|
- `@earendil-works/pi-ai/providers/<provider>` imports that provider's catalog and lazy API wrapper only.
|
|
1399
1671
|
- `@earendil-works/pi-ai/providers/all` imports every built-in provider factory and all catalogs. Use it only when you want the full built-in set.
|
|
1400
1672
|
- With code splitting, provider SDKs stay in lazy chunks and load on first request.
|
|
@@ -1458,11 +1730,12 @@ Use this when one process needs different provider settings per request, or when
|
|
|
1458
1730
|
Several providers support OAuth authentication instead of static API keys:
|
|
1459
1731
|
|
|
1460
1732
|
- **Anthropic** (Claude Pro/Max subscription)
|
|
1461
|
-
- **OpenAI
|
|
1733
|
+
- **OpenAI** (Sign in with ChatGPT: uses the ChatGPT subscription with the OpenAI API)
|
|
1734
|
+
- **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
|
|
1462
1735
|
- **GitHub Copilot** (Copilot subscription)
|
|
1463
1736
|
- **OpenRouter** (OAuth PKCE that mints a user-controlled API key)
|
|
1464
1737
|
|
|
1465
|
-
Each of these providers carries an `OAuthAuth` on `provider.auth.oauth` with three operations: `login(interaction)` uses the provider-neutral `AuthInteraction.prompt()`/`notify()` protocol and returns a credential, `refresh(credential)` refreshes expiring credentials when applicable, and `toAuth(credential)` derives request auth (GitHub Copilot's per-account base URL comes from here). Refresh is automatic: `models.getAuth(providerId)` and request paths refresh expired tokens under a credential-store lock, so concurrent requests and processes cannot double-refresh. OpenRouter's OAuth flow instead returns a permanent API key, so its refresh operation is a no-op.
|
|
1738
|
+
Each of these providers carries an `OAuthAuth` on `provider.auth.oauth` with three operations: `login(interaction)` uses the provider-neutral `AuthInteraction.prompt()`/`notify()` protocol and returns a credential, `refresh(credential, signal)` refreshes expiring credentials when applicable, and `toAuth(credential)` derives request auth (GitHub Copilot's per-account base URL comes from here). Provider login interactions and refresh calls always carry a concrete abort signal. Refresh is automatic: `models.getAuth(providerId)` and request paths refresh expired tokens under a credential-store lock, so concurrent requests and processes cannot double-refresh. OpenRouter's OAuth flow instead returns a permanent API key, so its refresh operation is a no-op.
|
|
1466
1739
|
|
|
1467
1740
|
```typescript
|
|
1468
1741
|
import { createModels } from '@earendil-works/pi-ai';
|
|
@@ -1538,7 +1811,7 @@ Built-in login and refresh flows are private provider implementations. Use provi
|
|
|
1538
1811
|
|
|
1539
1812
|
Provider notes:
|
|
1540
1813
|
|
|
1541
|
-
**OpenAI Codex**: Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity.
|
|
1814
|
+
**OpenAI Codex (legacy)**: Superseded by Sign in with ChatGPT on the OpenAI provider. Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity. Call `cleanupSessionResources(sessionId)` when finished so the pooled connection does not keep the process alive.
|
|
1542
1815
|
|
|
1543
1816
|
**Azure OpenAI (Responses)**: Uses the Responses API only. Set `AZURE_OPENAI_API_KEY` and either `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME`. `AZURE_OPENAI_BASE_URL` supports both `https://<resource>.openai.azure.com` and `https://<resource>.cognitiveservices.azure.com`; root endpoints are normalized to `.../openai/v1` automatically. Use `AZURE_OPENAI_API_VERSION` (defaults to `v1`) to override the API version if needed. Deployment names are treated as model IDs by default, override with `azureDeploymentName` or `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` using comma-separated `model-id=deployment` pairs (for example `gpt-4o-mini=my-deployment,gpt-4o=prod`). Legacy deployment-based URLs are intentionally unsupported.
|
|
1544
1817
|
|
|
@@ -1567,6 +1840,9 @@ Compat is a strict superset of the root entrypoint, so a file can switch its imp
|
|
|
1567
1840
|
| `getEnvApiKey('openai')` | `await models.getAuth(model.provider)` |
|
|
1568
1841
|
| `streamAnthropic(model, ctx, opts)` | `stream` from `@earendil-works/pi-ai/api/anthropic-messages`, or a provider in a collection |
|
|
1569
1842
|
| `registerFauxProvider()` | `fauxProvider()` + `models.setProvider()` |
|
|
1843
|
+
| `getImageModel('openrouter', id)` / `generateImages(model, ctx, { apiKey })` | `models.getModelOfType('image', 'openrouter', id)` / `models.generateImages(model, ctx)` |
|
|
1844
|
+
|
|
1845
|
+
The separate `ImagesModels`/`ImagesProvider` collection that existed briefly (`createImagesModels()`, `createImagesProvider()`, `openrouterImagesProvider()`, `builtinImagesModels()`) is gone: image models now live on the regular provider. Replace `builtinImagesModels()` with `builtinModels()`, `imagesModels.getModel()` with `models.getModelOfType('image', ...)`, and `createImagesProvider({ models, api })` with `createProvider({ models, images })`. The old plural image type names are removed; use `ImageModel` and `ImageApi`, and add `type: "image"` to image model literals.
|
|
1570
1846
|
|
|
1571
1847
|
## Development
|
|
1572
1848
|
|
|
@@ -1585,17 +1861,17 @@ Adding a new LLM provider requires changes across multiple files. The layered la
|
|
|
1585
1861
|
Create a new API implementation file (for example `bedrock-converse-stream.ts`) that exports exactly `stream` and `streamSimple`, plus:
|
|
1586
1862
|
|
|
1587
1863
|
- An options interface extending `StreamOptions` (for example `BedrockOptions`)
|
|
1588
|
-
- Message conversion functions to transform `
|
|
1864
|
+
- Message conversion functions to transform the `TranscriptContext` messages to provider format; read the prompt and tools from the transcript with `getInitialSystemMessage()`, `getCurrentTools()`, and `resolveTranscript()`
|
|
1589
1865
|
- Tool conversion if the provider supports tools
|
|
1590
1866
|
- Response parsing to emit standardized events (`text`, `tool_call`, `thinking`, `usage`, `stop`)
|
|
1591
1867
|
|
|
1592
1868
|
Add a lazy wrapper `src/api/<api-id>.lazy.ts` (`<name>Api()` via `lazyApi()`) so providers can reference the implementation without importing its SDK. Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@earendil-works/pi-ai`.
|
|
1593
1869
|
|
|
1594
|
-
#### 3. Model Generation (`scripts/generate-models.ts
|
|
1870
|
+
#### 3. Model Generation (`scripts/generate-models.ts`)
|
|
1595
1871
|
|
|
1596
1872
|
- Add logic to fetch and parse models from the provider's source (e.g., models.dev API)
|
|
1597
|
-
- Map chat/tool-capable provider
|
|
1598
|
-
-
|
|
1873
|
+
- Map chat/tool-capable provider data to `Model`, image-generation data to `ImageModel`, and models.dev `type: "decision"` entries to `ClassifierModel`; hydration groups the ignored `src/providers/data/<id>.json` values by API while stable `src/providers/<id>.models.ts` wrappers derive exact model/API types directly from those JSON keys
|
|
1874
|
+
- Keep model ids unique within each provider and model type; emit separate entries when an upstream model supports multiple operations
|
|
1599
1875
|
- Handle provider-specific quirks (pricing format, capability flags, model ID transformations)
|
|
1600
1876
|
|
|
1601
1877
|
#### 4. Provider Factory (`src/providers/<id>.ts`)
|