@sammorrowdrums/mcpi-ai 0.70.8 → 0.85.0-bootstrap.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +845 -486
- package/dist/api/anthropic-messages.d.ts +71 -0
- package/dist/api/anthropic-messages.d.ts.map +1 -0
- package/dist/api/anthropic-messages.js +1060 -0
- package/dist/api/anthropic-messages.js.map +1 -0
- package/dist/api/anthropic-messages.lazy.d.ts +3 -0
- package/dist/api/anthropic-messages.lazy.d.ts.map +1 -0
- package/dist/api/anthropic-messages.lazy.js +3 -0
- package/dist/api/anthropic-messages.lazy.js.map +1 -0
- package/dist/api/azure-openai-responses.d.ts +15 -0
- package/dist/api/azure-openai-responses.d.ts.map +1 -0
- package/dist/api/azure-openai-responses.js +242 -0
- package/dist/api/azure-openai-responses.js.map +1 -0
- package/dist/api/azure-openai-responses.lazy.d.ts +3 -0
- package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -0
- package/dist/api/azure-openai-responses.lazy.js +3 -0
- package/dist/api/azure-openai-responses.lazy.js.map +1 -0
- package/dist/api/bedrock-converse-stream.d.ts +38 -0
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -0
- package/dist/api/bedrock-converse-stream.js +967 -0
- package/dist/api/bedrock-converse-stream.js.map +1 -0
- package/dist/api/bedrock-converse-stream.lazy.d.ts +9 -0
- package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -0
- package/dist/api/bedrock-converse-stream.lazy.js +30 -0
- package/dist/api/bedrock-converse-stream.lazy.js.map +1 -0
- package/dist/api/cloudflare-gateway-binding.d.ts +69 -0
- package/dist/api/cloudflare-gateway-binding.d.ts.map +1 -0
- package/dist/api/cloudflare-gateway-binding.js +159 -0
- package/dist/api/cloudflare-gateway-binding.js.map +1 -0
- package/dist/api/cloudflare.d.ts +9 -0
- package/dist/api/cloudflare.d.ts.map +1 -0
- package/dist/api/cloudflare.js +9 -0
- package/dist/api/cloudflare.js.map +1 -0
- package/dist/api/constrained-sampling.d.ts +20 -0
- package/dist/api/constrained-sampling.d.ts.map +1 -0
- package/dist/api/constrained-sampling.js +222 -0
- package/dist/api/constrained-sampling.js.map +1 -0
- package/dist/{providers → api}/github-copilot-headers.d.ts +1 -1
- package/dist/api/github-copilot-headers.d.ts.map +1 -0
- package/dist/api/github-copilot-headers.js.map +1 -0
- package/dist/api/google-generative-ai.d.ts +13 -0
- package/dist/api/google-generative-ai.d.ts.map +1 -0
- package/dist/api/google-generative-ai.js +417 -0
- package/dist/api/google-generative-ai.js.map +1 -0
- package/dist/api/google-generative-ai.lazy.d.ts +3 -0
- package/dist/api/google-generative-ai.lazy.d.ts.map +1 -0
- package/dist/api/google-generative-ai.lazy.js +3 -0
- package/dist/api/google-generative-ai.lazy.js.map +1 -0
- package/dist/{providers → api}/google-shared.d.ts +23 -7
- package/dist/api/google-shared.d.ts.map +1 -0
- package/dist/{providers → api}/google-shared.js +76 -30
- package/dist/api/google-shared.js.map +1 -0
- package/dist/api/google-vertex.d.ts +15 -0
- package/dist/api/google-vertex.d.ts.map +1 -0
- package/dist/api/google-vertex.js +466 -0
- package/dist/api/google-vertex.js.map +1 -0
- package/dist/api/google-vertex.lazy.d.ts +3 -0
- package/dist/api/google-vertex.lazy.d.ts.map +1 -0
- package/dist/api/google-vertex.lazy.js +3 -0
- package/dist/api/google-vertex.lazy.js.map +1 -0
- package/dist/api/lazy.d.ts +19 -0
- package/dist/api/lazy.d.ts.map +1 -0
- package/dist/api/lazy.js +70 -0
- package/dist/api/lazy.js.map +1 -0
- package/dist/api/mistral-conversations.d.ts +25 -0
- package/dist/api/mistral-conversations.d.ts.map +1 -0
- package/dist/api/mistral-conversations.js +736 -0
- package/dist/api/mistral-conversations.js.map +1 -0
- package/dist/api/mistral-conversations.lazy.d.ts +3 -0
- package/dist/api/mistral-conversations.lazy.d.ts.map +1 -0
- package/dist/api/mistral-conversations.lazy.js +3 -0
- package/dist/api/mistral-conversations.lazy.js.map +1 -0
- package/dist/api/openai-codex-responses.d.ts +31 -0
- package/dist/api/openai-codex-responses.d.ts.map +1 -0
- package/dist/api/openai-codex-responses.js +1294 -0
- package/dist/api/openai-codex-responses.js.map +1 -0
- package/dist/api/openai-codex-responses.lazy.d.ts +3 -0
- package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -0
- package/dist/api/openai-codex-responses.lazy.js +3 -0
- package/dist/api/openai-codex-responses.lazy.js.map +1 -0
- package/dist/api/openai-completions.d.ts +22 -0
- package/dist/api/openai-completions.d.ts.map +1 -0
- package/dist/api/openai-completions.js +1279 -0
- package/dist/api/openai-completions.js.map +1 -0
- package/dist/api/openai-completions.lazy.d.ts +3 -0
- package/dist/api/openai-completions.lazy.d.ts.map +1 -0
- package/dist/api/openai-completions.lazy.js +3 -0
- package/dist/api/openai-completions.lazy.js.map +1 -0
- package/dist/api/openai-prompt-cache.d.ts +3 -0
- package/dist/api/openai-prompt-cache.d.ts.map +1 -0
- package/dist/api/openai-prompt-cache.js +10 -0
- package/dist/api/openai-prompt-cache.js.map +1 -0
- package/dist/{providers → api}/openai-responses-shared.d.ts +11 -3
- package/dist/api/openai-responses-shared.d.ts.map +1 -0
- package/dist/api/openai-responses-shared.js +686 -0
- package/dist/api/openai-responses-shared.js.map +1 -0
- package/dist/{providers → api}/openai-responses.d.ts +5 -4
- package/dist/api/openai-responses.d.ts.map +1 -0
- package/dist/api/openai-responses.js +283 -0
- package/dist/api/openai-responses.js.map +1 -0
- package/dist/api/openai-responses.lazy.d.ts +3 -0
- package/dist/api/openai-responses.lazy.d.ts.map +1 -0
- package/dist/api/openai-responses.lazy.js +3 -0
- package/dist/api/openai-responses.lazy.js.map +1 -0
- package/dist/api/openrouter-images.d.ts +3 -0
- package/dist/api/openrouter-images.d.ts.map +1 -0
- package/dist/api/openrouter-images.js +132 -0
- package/dist/api/openrouter-images.js.map +1 -0
- package/dist/api/openrouter-images.lazy.d.ts +3 -0
- package/dist/api/openrouter-images.lazy.d.ts.map +1 -0
- package/dist/api/openrouter-images.lazy.js +4 -0
- package/dist/api/openrouter-images.lazy.js.map +1 -0
- package/dist/api/pi-messages.d.ts +97 -0
- package/dist/api/pi-messages.d.ts.map +1 -0
- package/dist/api/pi-messages.js +307 -0
- package/dist/api/pi-messages.js.map +1 -0
- package/dist/api/pi-messages.lazy.d.ts +3 -0
- package/dist/api/pi-messages.lazy.d.ts.map +1 -0
- package/dist/api/pi-messages.lazy.js +3 -0
- package/dist/api/pi-messages.lazy.js.map +1 -0
- package/dist/api/simple-options.d.ts +11 -0
- package/dist/api/simple-options.d.ts.map +1 -0
- package/dist/api/simple-options.js +59 -0
- package/dist/api/simple-options.js.map +1 -0
- package/dist/{providers → api}/transform-messages.d.ts +1 -1
- package/dist/api/transform-messages.d.ts.map +1 -0
- package/dist/{providers → api}/transform-messages.js +4 -1
- package/dist/api/transform-messages.js.map +1 -0
- package/dist/auth/context.d.ts +7 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/context.js +42 -0
- package/dist/auth/context.js.map +1 -0
- package/dist/auth/credential-store.d.ts +17 -0
- package/dist/auth/credential-store.d.ts.map +1 -0
- package/dist/auth/credential-store.js +51 -0
- package/dist/auth/credential-store.js.map +1 -0
- package/dist/auth/helpers.d.ts +22 -0
- package/dist/auth/helpers.d.ts.map +1 -0
- package/dist/auth/helpers.js +53 -0
- package/dist/auth/helpers.js.map +1 -0
- package/dist/auth/oauth/anthropic.d.ts +9 -0
- package/dist/auth/oauth/anthropic.d.ts.map +1 -0
- package/dist/{utils → auth}/oauth/anthropic.js +66 -93
- package/dist/auth/oauth/anthropic.js.map +1 -0
- package/dist/auth/oauth/device-code.d.ts +23 -0
- package/dist/auth/oauth/device-code.d.ts.map +1 -0
- package/dist/auth/oauth/device-code.js +69 -0
- package/dist/auth/oauth/device-code.js.map +1 -0
- package/dist/auth/oauth/github-copilot.d.ts +6 -0
- package/dist/auth/oauth/github-copilot.d.ts.map +1 -0
- package/dist/auth/oauth/github-copilot.js +328 -0
- package/dist/auth/oauth/github-copilot.js.map +1 -0
- package/dist/auth/oauth/kimi-coding.d.ts +10 -0
- package/dist/auth/oauth/kimi-coding.d.ts.map +1 -0
- package/dist/auth/oauth/kimi-coding.js +261 -0
- package/dist/auth/oauth/kimi-coding.js.map +1 -0
- package/dist/auth/oauth/load.d.ts +27 -0
- package/dist/auth/oauth/load.d.ts.map +1 -0
- package/dist/auth/oauth/load.js +59 -0
- package/dist/auth/oauth/load.js.map +1 -0
- package/dist/auth/oauth/oauth-page.d.ts.map +1 -0
- package/dist/auth/oauth/oauth-page.js.map +1 -0
- package/dist/auth/oauth/openai-codex.d.ts +9 -0
- package/dist/auth/oauth/openai-codex.d.ts.map +1 -0
- package/dist/auth/oauth/openai-codex.js +450 -0
- package/dist/auth/oauth/openai-codex.js.map +1 -0
- package/dist/auth/oauth/openrouter.d.ts +15 -0
- package/dist/auth/oauth/openrouter.d.ts.map +1 -0
- package/dist/auth/oauth/openrouter.js +277 -0
- package/dist/auth/oauth/openrouter.js.map +1 -0
- package/dist/auth/oauth/pkce.d.ts.map +1 -0
- package/dist/auth/oauth/pkce.js.map +1 -0
- package/dist/auth/oauth/radius.d.ts +17 -0
- package/dist/auth/oauth/radius.d.ts.map +1 -0
- package/dist/auth/oauth/radius.js +321 -0
- package/dist/auth/oauth/radius.js.map +1 -0
- package/dist/auth/oauth/xai.d.ts +6 -0
- package/dist/auth/oauth/xai.d.ts.map +1 -0
- package/dist/auth/oauth/xai.js +190 -0
- package/dist/auth/oauth/xai.js.map +1 -0
- package/dist/auth/resolve.d.ts +27 -0
- package/dist/auth/resolve.d.ts.map +1 -0
- package/dist/auth/resolve.js +132 -0
- package/dist/auth/resolve.js.map +1 -0
- package/dist/auth/types.d.ts +231 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/bedrock-provider.d.ts +2 -2
- package/dist/bedrock-provider.d.ts.map +1 -1
- package/dist/bedrock-provider.js +3 -3
- package/dist/bedrock-provider.js.map +1 -1
- package/dist/bun-oauth.d.ts +3 -0
- package/dist/bun-oauth.d.ts.map +1 -0
- package/dist/bun-oauth.js +21 -0
- package/dist/bun-oauth.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +60 -63
- package/dist/cli.js.map +1 -1
- package/dist/compat/extension-oauth-types.d.ts +39 -0
- package/dist/compat/extension-oauth-types.d.ts.map +1 -0
- package/dist/compat/extension-oauth-types.js +2 -0
- package/dist/compat/extension-oauth-types.js.map +1 -0
- package/dist/compat.d.ts +67 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +199 -0
- package/dist/compat.js.map +1 -0
- package/dist/env-api-keys.d.ts +8 -5
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +60 -22
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.d.ts +10 -0
- package/dist/image-models.d.ts.map +1 -0
- package/dist/image-models.generated.d.ts +740 -0
- package/dist/image-models.generated.d.ts.map +1 -0
- package/dist/image-models.generated.js +742 -0
- package/dist/image-models.generated.js.map +1 -0
- package/dist/image-models.js +23 -0
- package/dist/image-models.js.map +1 -0
- package/dist/images-api-registry.d.ts +14 -0
- package/dist/images-api-registry.d.ts.map +1 -0
- package/dist/images-api-registry.js +22 -0
- package/dist/images-api-registry.js.map +1 -0
- package/dist/images-models.d.ts +95 -0
- package/dist/images-models.d.ts.map +1 -0
- package/dist/images-models.js +143 -0
- package/dist/images-models.js.map +1 -0
- package/dist/images.d.ts +4 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +14 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +32 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/legacy-api-aliases.d.ts +42 -0
- package/dist/legacy-api-aliases.d.ts.map +1 -0
- package/dist/legacy-api-aliases.js +49 -0
- package/dist/legacy-api-aliases.js.map +1 -0
- package/dist/model-catalog.d.ts +17 -0
- package/dist/model-catalog.d.ts.map +1 -0
- package/dist/model-catalog.js +4 -0
- package/dist/model-catalog.js.map +1 -0
- package/dist/models-store.d.ts +29 -0
- package/dist/models-store.d.ts.map +1 -0
- package/dist/models-store.js +17 -0
- package/dist/models-store.js.map +1 -0
- package/dist/models.d.ts +191 -15
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +78 -15427
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +78 -15235
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +565 -38
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts +2 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +1 -1
- package/dist/oauth.js.map +1 -1
- package/dist/providers/all.d.ts +28 -0
- package/dist/providers/all.d.ts.map +1 -0
- package/dist/providers/all.js +131 -0
- package/dist/providers/all.js.map +1 -0
- package/dist/providers/amazon-bedrock.d.ts +2 -37
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +81 -724
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/amazon-bedrock.models.d.ts +4 -0
- package/dist/providers/amazon-bedrock.models.d.ts.map +1 -0
- package/dist/providers/amazon-bedrock.models.js +6 -0
- package/dist/providers/amazon-bedrock.models.js.map +1 -0
- package/dist/providers/ant-ling.d.ts +3 -0
- package/dist/providers/ant-ling.d.ts.map +1 -0
- package/dist/providers/ant-ling.js +15 -0
- package/dist/providers/ant-ling.js.map +1 -0
- package/dist/providers/ant-ling.models.d.ts +4 -0
- package/dist/providers/ant-ling.models.d.ts.map +1 -0
- package/dist/providers/ant-ling.models.js +6 -0
- package/dist/providers/ant-ling.models.js.map +1 -0
- package/dist/providers/anthropic.d.ts +2 -53
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +48 -940
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/anthropic.models.d.ts +4 -0
- package/dist/providers/anthropic.models.d.ts.map +1 -0
- package/dist/providers/anthropic.models.js +6 -0
- package/dist/providers/anthropic.models.js.map +1 -0
- package/dist/providers/azure-openai-responses.d.ts +2 -14
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +11 -184
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/azure-openai-responses.models.d.ts +4 -0
- package/dist/providers/azure-openai-responses.models.d.ts.map +1 -0
- package/dist/providers/azure-openai-responses.models.js +6 -0
- package/dist/providers/azure-openai-responses.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 +4 -0
- package/dist/providers/baseten.models.d.ts.map +1 -0
- package/dist/providers/baseten.models.js +6 -0
- package/dist/providers/baseten.models.js.map +1 -0
- package/dist/providers/cerebras.d.ts +3 -0
- package/dist/providers/cerebras.d.ts.map +1 -0
- package/dist/providers/cerebras.js +15 -0
- package/dist/providers/cerebras.js.map +1 -0
- package/dist/providers/cerebras.models.d.ts +4 -0
- package/dist/providers/cerebras.models.d.ts.map +1 -0
- package/dist/providers/cerebras.models.js +6 -0
- package/dist/providers/cerebras.models.js.map +1 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts +3 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -0
- package/dist/providers/cloudflare-ai-gateway.js +21 -0
- package/dist/providers/cloudflare-ai-gateway.js.map +1 -0
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -0
- package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -0
- package/dist/providers/cloudflare-ai-gateway.models.js +6 -0
- package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -0
- package/dist/providers/cloudflare-auth.d.ts +4 -0
- package/dist/providers/cloudflare-auth.d.ts.map +1 -0
- package/dist/providers/cloudflare-auth.js +86 -0
- package/dist/providers/cloudflare-auth.js.map +1 -0
- package/dist/providers/cloudflare-stream.d.ts +8 -0
- package/dist/providers/cloudflare-stream.d.ts.map +1 -0
- package/dist/providers/cloudflare-stream.js +21 -0
- package/dist/providers/cloudflare-stream.js.map +1 -0
- package/dist/providers/cloudflare-workers-ai.d.ts +3 -0
- package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -0
- package/dist/providers/cloudflare-workers-ai.js +15 -0
- package/dist/providers/cloudflare-workers-ai.js.map +1 -0
- package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -0
- package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -0
- package/dist/providers/cloudflare-workers-ai.models.js +6 -0
- package/dist/providers/cloudflare-workers-ai.models.js.map +1 -0
- package/dist/providers/data/.manifest.json +1 -0
- package/dist/providers/data/amazon-bedrock.json +1 -0
- package/dist/providers/data/ant-ling.json +1 -0
- package/dist/providers/data/anthropic.json +1 -0
- package/dist/providers/data/azure-openai-responses.json +1 -0
- package/dist/providers/data/baseten.json +1 -0
- package/dist/providers/data/cerebras.json +1 -0
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -0
- package/dist/providers/data/cloudflare-workers-ai.json +1 -0
- package/dist/providers/data/deepseek.json +1 -0
- package/dist/providers/data/fireworks.json +1 -0
- package/dist/providers/data/github-copilot.json +1 -0
- package/dist/providers/data/google-vertex.json +1 -0
- package/dist/providers/data/google.json +1 -0
- package/dist/providers/data/groq.json +1 -0
- package/dist/providers/data/huggingface.json +1 -0
- package/dist/providers/data/kimi-coding.json +1 -0
- package/dist/providers/data/minimax-cn.json +1 -0
- package/dist/providers/data/minimax.json +1 -0
- package/dist/providers/data/mistral.json +1 -0
- package/dist/providers/data/moonshotai-cn.json +1 -0
- package/dist/providers/data/moonshotai.json +1 -0
- package/dist/providers/data/nvidia.json +1 -0
- package/dist/providers/data/openai-codex.json +1 -0
- package/dist/providers/data/openai.json +1 -0
- package/dist/providers/data/opencode-go.json +1 -0
- package/dist/providers/data/opencode.json +1 -0
- package/dist/providers/data/openrouter.json +1 -0
- package/dist/providers/data/qwen-token-plan-cn.json +1 -0
- package/dist/providers/data/qwen-token-plan-individual.json +1 -0
- package/dist/providers/data/qwen-token-plan.json +1 -0
- package/dist/providers/data/together.json +1 -0
- package/dist/providers/data/vercel-ai-gateway.json +1 -0
- package/dist/providers/data/xai.json +1 -0
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -0
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -0
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -0
- package/dist/providers/data/xiaomi.json +1 -0
- package/dist/providers/data/zai-coding-cn.json +1 -0
- package/dist/providers/data/zai.json +1 -0
- package/dist/providers/deepseek.d.ts +3 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +15 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/deepseek.models.d.ts +4 -0
- package/dist/providers/deepseek.models.d.ts.map +1 -0
- package/dist/providers/deepseek.models.js +6 -0
- package/dist/providers/deepseek.models.js.map +1 -0
- package/dist/providers/faux.d.ts +53 -7
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +131 -11
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/fireworks.d.ts +3 -0
- package/dist/providers/fireworks.d.ts.map +1 -0
- package/dist/providers/fireworks.js +19 -0
- package/dist/providers/fireworks.js.map +1 -0
- package/dist/providers/fireworks.models.d.ts +4 -0
- package/dist/providers/fireworks.models.d.ts.map +1 -0
- package/dist/providers/fireworks.models.js +6 -0
- package/dist/providers/fireworks.models.js.map +1 -0
- package/dist/providers/github-copilot.d.ts +3 -0
- package/dist/providers/github-copilot.d.ts.map +1 -0
- package/dist/providers/github-copilot.js +35 -0
- package/dist/providers/github-copilot.js.map +1 -0
- package/dist/providers/github-copilot.models.d.ts +4 -0
- package/dist/providers/github-copilot.models.d.ts.map +1 -0
- package/dist/providers/github-copilot.models.js +6 -0
- package/dist/providers/github-copilot.models.js.map +1 -0
- package/dist/providers/google-vertex.d.ts +2 -14
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +86 -433
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google-vertex.models.d.ts +4 -0
- package/dist/providers/google-vertex.models.d.ts.map +1 -0
- package/dist/providers/google-vertex.models.js +6 -0
- package/dist/providers/google-vertex.models.js.map +1 -0
- package/dist/providers/google.d.ts +2 -12
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +12 -396
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/google.models.d.ts +4 -0
- package/dist/providers/google.models.d.ts.map +1 -0
- package/dist/providers/google.models.js +6 -0
- package/dist/providers/google.models.js.map +1 -0
- package/dist/providers/groq.d.ts +3 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +15 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/groq.models.d.ts +4 -0
- package/dist/providers/groq.models.d.ts.map +1 -0
- package/dist/providers/groq.models.js +6 -0
- package/dist/providers/groq.models.js.map +1 -0
- package/dist/providers/huggingface.d.ts +3 -0
- package/dist/providers/huggingface.d.ts.map +1 -0
- package/dist/providers/huggingface.js +15 -0
- package/dist/providers/huggingface.js.map +1 -0
- package/dist/providers/huggingface.models.d.ts +4 -0
- package/dist/providers/huggingface.models.d.ts.map +1 -0
- package/dist/providers/huggingface.models.js +6 -0
- package/dist/providers/huggingface.models.js.map +1 -0
- package/dist/providers/images/register-builtins.d.ts +4 -0
- package/dist/providers/images/register-builtins.d.ts.map +1 -0
- package/dist/providers/images/register-builtins.js +34 -0
- package/dist/providers/images/register-builtins.js.map +1 -0
- package/dist/providers/kimi-coding.d.ts +3 -0
- package/dist/providers/kimi-coding.d.ts.map +1 -0
- package/dist/providers/kimi-coding.js +24 -0
- package/dist/providers/kimi-coding.js.map +1 -0
- package/dist/providers/kimi-coding.models.d.ts +4 -0
- package/dist/providers/kimi-coding.models.d.ts.map +1 -0
- package/dist/providers/kimi-coding.models.js +6 -0
- package/dist/providers/kimi-coding.models.js.map +1 -0
- package/dist/providers/minimax-cn.d.ts +3 -0
- package/dist/providers/minimax-cn.d.ts.map +1 -0
- package/dist/providers/minimax-cn.js +15 -0
- package/dist/providers/minimax-cn.js.map +1 -0
- package/dist/providers/minimax-cn.models.d.ts +4 -0
- package/dist/providers/minimax-cn.models.d.ts.map +1 -0
- package/dist/providers/minimax-cn.models.js +6 -0
- package/dist/providers/minimax-cn.models.js.map +1 -0
- package/dist/providers/minimax.d.ts +3 -0
- package/dist/providers/minimax.d.ts.map +1 -0
- package/dist/providers/minimax.js +15 -0
- package/dist/providers/minimax.js.map +1 -0
- package/dist/providers/minimax.models.d.ts +4 -0
- package/dist/providers/minimax.models.d.ts.map +1 -0
- package/dist/providers/minimax.models.js +6 -0
- package/dist/providers/minimax.models.js.map +1 -0
- package/dist/providers/mistral.d.ts +2 -24
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +12 -531
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/mistral.models.d.ts +4 -0
- package/dist/providers/mistral.models.d.ts.map +1 -0
- package/dist/providers/mistral.models.js +6 -0
- package/dist/providers/mistral.models.js.map +1 -0
- package/dist/providers/moonshotai-cn.d.ts +3 -0
- package/dist/providers/moonshotai-cn.d.ts.map +1 -0
- package/dist/providers/moonshotai-cn.js +15 -0
- package/dist/providers/moonshotai-cn.js.map +1 -0
- package/dist/providers/moonshotai-cn.models.d.ts +4 -0
- package/dist/providers/moonshotai-cn.models.d.ts.map +1 -0
- package/dist/providers/moonshotai-cn.models.js +6 -0
- package/dist/providers/moonshotai-cn.models.js.map +1 -0
- package/dist/providers/moonshotai.d.ts +3 -0
- package/dist/providers/moonshotai.d.ts.map +1 -0
- package/dist/providers/moonshotai.js +15 -0
- package/dist/providers/moonshotai.js.map +1 -0
- package/dist/providers/moonshotai.models.d.ts +4 -0
- package/dist/providers/moonshotai.models.d.ts.map +1 -0
- package/dist/providers/moonshotai.models.js +6 -0
- package/dist/providers/moonshotai.models.js.map +1 -0
- package/dist/providers/nvidia.d.ts +3 -0
- package/dist/providers/nvidia.d.ts.map +1 -0
- package/dist/providers/nvidia.js +15 -0
- package/dist/providers/nvidia.js.map +1 -0
- package/dist/providers/nvidia.models.d.ts +4 -0
- package/dist/providers/nvidia.models.d.ts.map +1 -0
- package/dist/providers/nvidia.models.js +6 -0
- package/dist/providers/nvidia.models.js.map +1 -0
- package/dist/providers/openai-codex.d.ts +3 -0
- package/dist/providers/openai-codex.d.ts.map +1 -0
- package/dist/providers/openai-codex.js +22 -0
- package/dist/providers/openai-codex.js.map +1 -0
- package/dist/providers/openai-codex.models.d.ts +4 -0
- package/dist/providers/openai-codex.models.d.ts.map +1 -0
- package/dist/providers/openai-codex.models.js +6 -0
- package/dist/providers/openai-codex.models.js.map +1 -0
- package/dist/providers/openai.d.ts +3 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +15 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openai.models.d.ts +4 -0
- package/dist/providers/openai.models.d.ts.map +1 -0
- package/dist/providers/openai.models.js +6 -0
- package/dist/providers/openai.models.js.map +1 -0
- package/dist/providers/opencode-go.d.ts +3 -0
- package/dist/providers/opencode-go.d.ts.map +1 -0
- package/dist/providers/opencode-go.js +20 -0
- package/dist/providers/opencode-go.js.map +1 -0
- package/dist/providers/opencode-go.models.d.ts +4 -0
- package/dist/providers/opencode-go.models.d.ts.map +1 -0
- package/dist/providers/opencode-go.models.js +6 -0
- package/dist/providers/opencode-go.models.js.map +1 -0
- package/dist/providers/opencode.d.ts +3 -0
- package/dist/providers/opencode.d.ts.map +1 -0
- package/dist/providers/opencode.js +22 -0
- package/dist/providers/opencode.js.map +1 -0
- package/dist/providers/opencode.models.d.ts +4 -0
- package/dist/providers/opencode.models.d.ts.map +1 -0
- package/dist/providers/opencode.models.js +6 -0
- package/dist/providers/opencode.models.js.map +1 -0
- package/dist/providers/openrouter-images.d.ts +3 -0
- package/dist/providers/openrouter-images.d.ts.map +1 -0
- package/dist/providers/openrouter-images.js +22 -0
- package/dist/providers/openrouter-images.js.map +1 -0
- package/dist/providers/openrouter.d.ts +3 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +23 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/openrouter.models.d.ts +4 -0
- package/dist/providers/openrouter.models.d.ts.map +1 -0
- package/dist/providers/openrouter.models.js +6 -0
- package/dist/providers/openrouter.models.js.map +1 -0
- package/dist/providers/qwen-token-plan-cn.d.ts +3 -0
- package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-cn.js +15 -0
- package/dist/providers/qwen-token-plan-cn.js.map +1 -0
- package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -0
- package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-cn.models.js +6 -0
- package/dist/providers/qwen-token-plan-cn.models.js.map +1 -0
- 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 +4 -0
- package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan-individual.models.js +6 -0
- package/dist/providers/qwen-token-plan-individual.models.js.map +1 -0
- package/dist/providers/qwen-token-plan.d.ts +3 -0
- package/dist/providers/qwen-token-plan.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan.js +15 -0
- package/dist/providers/qwen-token-plan.js.map +1 -0
- package/dist/providers/qwen-token-plan.models.d.ts +4 -0
- package/dist/providers/qwen-token-plan.models.d.ts.map +1 -0
- package/dist/providers/qwen-token-plan.models.js +6 -0
- package/dist/providers/qwen-token-plan.models.js.map +1 -0
- package/dist/providers/radius-config.d.ts +26 -0
- package/dist/providers/radius-config.d.ts.map +1 -0
- package/dist/providers/radius-config.js +63 -0
- package/dist/providers/radius-config.js.map +1 -0
- package/dist/providers/radius.d.ts +9 -0
- package/dist/providers/radius.d.ts.map +1 -0
- package/dist/providers/radius.js +64 -0
- package/dist/providers/radius.js.map +1 -0
- package/dist/providers/together.d.ts +3 -0
- package/dist/providers/together.d.ts.map +1 -0
- package/dist/providers/together.js +15 -0
- package/dist/providers/together.js.map +1 -0
- package/dist/providers/together.models.d.ts +4 -0
- package/dist/providers/together.models.d.ts.map +1 -0
- package/dist/providers/together.models.js +6 -0
- package/dist/providers/together.models.js.map +1 -0
- package/dist/providers/vercel-ai-gateway.d.ts +3 -0
- package/dist/providers/vercel-ai-gateway.d.ts.map +1 -0
- package/dist/providers/vercel-ai-gateway.js +15 -0
- package/dist/providers/vercel-ai-gateway.js.map +1 -0
- package/dist/providers/vercel-ai-gateway.models.d.ts +4 -0
- package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -0
- package/dist/providers/vercel-ai-gateway.models.js +6 -0
- package/dist/providers/vercel-ai-gateway.models.js.map +1 -0
- package/dist/providers/xai.d.ts +3 -0
- package/dist/providers/xai.d.ts.map +1 -0
- package/dist/providers/xai.js +28 -0
- package/dist/providers/xai.js.map +1 -0
- package/dist/providers/xai.models.d.ts +4 -0
- package/dist/providers/xai.models.d.ts.map +1 -0
- package/dist/providers/xai.models.js +6 -0
- package/dist/providers/xai.models.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-ams.d.ts +3 -0
- package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-ams.js +15 -0
- package/dist/providers/xiaomi-token-plan-ams.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -0
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-ams.models.js +6 -0
- package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-cn.d.ts +3 -0
- package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-cn.js +15 -0
- package/dist/providers/xiaomi-token-plan-cn.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -0
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-cn.models.js +6 -0
- package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-sgp.d.ts +3 -0
- package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-sgp.js +15 -0
- package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -0
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -0
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -0
- package/dist/providers/xiaomi-token-plan-sgp.models.js +6 -0
- package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -0
- package/dist/providers/xiaomi.d.ts +3 -0
- package/dist/providers/xiaomi.d.ts.map +1 -0
- package/dist/providers/xiaomi.js +15 -0
- package/dist/providers/xiaomi.js.map +1 -0
- package/dist/providers/xiaomi.models.d.ts +4 -0
- package/dist/providers/xiaomi.models.d.ts.map +1 -0
- package/dist/providers/xiaomi.models.js +6 -0
- package/dist/providers/xiaomi.models.js.map +1 -0
- package/dist/providers/zai-coding-cn.d.ts +3 -0
- package/dist/providers/zai-coding-cn.d.ts.map +1 -0
- package/dist/providers/zai-coding-cn.js +15 -0
- package/dist/providers/zai-coding-cn.js.map +1 -0
- package/dist/providers/zai-coding-cn.models.d.ts +4 -0
- package/dist/providers/zai-coding-cn.models.d.ts.map +1 -0
- package/dist/providers/zai-coding-cn.models.js +6 -0
- package/dist/providers/zai-coding-cn.models.js.map +1 -0
- package/dist/providers/zai.d.ts +3 -0
- package/dist/providers/zai.d.ts.map +1 -0
- package/dist/providers/zai.js +15 -0
- package/dist/providers/zai.js.map +1 -0
- package/dist/providers/zai.models.d.ts +4 -0
- package/dist/providers/zai.models.d.ts.map +1 -0
- package/dist/providers/zai.models.js +6 -0
- package/dist/providers/zai.models.js.map +1 -0
- package/dist/session-resources.d.ts +4 -0
- package/dist/session-resources.d.ts.map +1 -0
- package/dist/session-resources.js +22 -0
- package/dist/session-resources.js.map +1 -0
- package/dist/types.d.ts +361 -49
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/abort-signals.d.ts +6 -0
- package/dist/utils/abort-signals.d.ts.map +1 -0
- package/dist/utils/abort-signals.js +34 -0
- package/dist/utils/abort-signals.js.map +1 -0
- 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/deferred-tools.d.ts +9 -0
- package/dist/utils/deferred-tools.d.ts.map +1 -0
- package/dist/utils/deferred-tools.js +36 -0
- package/dist/utils/deferred-tools.js.map +1 -0
- package/dist/utils/diagnostics.d.ts +19 -0
- package/dist/utils/diagnostics.d.ts.map +1 -0
- package/dist/utils/diagnostics.js +25 -0
- package/dist/utils/diagnostics.js.map +1 -0
- package/dist/utils/error-body.d.ts +25 -0
- package/dist/utils/error-body.d.ts.map +1 -0
- package/dist/utils/error-body.js +133 -0
- package/dist/utils/error-body.js.map +1 -0
- package/dist/utils/estimate.d.ts +17 -0
- package/dist/utils/estimate.d.ts.map +1 -0
- package/dist/utils/estimate.js +116 -0
- package/dist/utils/estimate.js.map +1 -0
- package/dist/utils/event-stream.d.ts +3 -3
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +2 -2
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/headers.d.ts +2 -0
- package/dist/utils/headers.d.ts.map +1 -1
- package/dist/utils/headers.js +10 -0
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/node-http-proxy.d.ts +4 -0
- package/dist/utils/node-http-proxy.d.ts.map +1 -0
- package/dist/utils/node-http-proxy.js +92 -0
- package/dist/utils/node-http-proxy.js.map +1 -0
- package/dist/utils/overflow.d.ts +19 -4
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +44 -4
- 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 +7 -0
- package/dist/utils/provider-env.d.ts.map +1 -0
- package/dist/utils/provider-env.js +44 -0
- package/dist/utils/provider-env.js.map +1 -0
- package/dist/utils/provider-retry.d.ts +16 -0
- package/dist/utils/provider-retry.d.ts.map +1 -0
- package/dist/utils/provider-retry.js +94 -0
- package/dist/utils/provider-retry.js.map +1 -0
- package/dist/utils/retry.d.ts +53 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +174 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/text.d.ts +6 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +10 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/uuid.d.ts +3 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +46 -0
- package/dist/utils/uuid.js.map +1 -0
- package/dist/utils/validation.d.ts +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +49 -21
- package/dist/utils/validation.js.map +1 -1
- package/package.json +52 -55
- package/dist/api-registry.d.ts +0 -20
- package/dist/api-registry.d.ts.map +0 -1
- package/dist/api-registry.js +0 -44
- package/dist/api-registry.js.map +0 -1
- package/dist/providers/github-copilot-headers.d.ts.map +0 -1
- package/dist/providers/github-copilot-headers.js.map +0 -1
- package/dist/providers/google-gemini-cli.d.ts +0 -74
- package/dist/providers/google-gemini-cli.d.ts.map +0 -1
- package/dist/providers/google-gemini-cli.js +0 -779
- package/dist/providers/google-gemini-cli.js.map +0 -1
- package/dist/providers/google-shared.d.ts.map +0 -1
- package/dist/providers/google-shared.js.map +0 -1
- package/dist/providers/openai-codex-responses.d.ts +0 -11
- package/dist/providers/openai-codex-responses.d.ts.map +0 -1
- package/dist/providers/openai-codex-responses.js +0 -779
- package/dist/providers/openai-codex-responses.js.map +0 -1
- package/dist/providers/openai-completions.d.ts +0 -19
- package/dist/providers/openai-completions.d.ts.map +0 -1
- package/dist/providers/openai-completions.js +0 -876
- package/dist/providers/openai-completions.js.map +0 -1
- package/dist/providers/openai-responses-shared.d.ts.map +0 -1
- package/dist/providers/openai-responses-shared.js +0 -483
- package/dist/providers/openai-responses-shared.js.map +0 -1
- package/dist/providers/openai-responses.d.ts.map +0 -1
- package/dist/providers/openai-responses.js +0 -208
- package/dist/providers/openai-responses.js.map +0 -1
- package/dist/providers/register-builtins.d.ts +0 -38
- package/dist/providers/register-builtins.d.ts.map +0 -1
- package/dist/providers/register-builtins.js +0 -261
- package/dist/providers/register-builtins.js.map +0 -1
- package/dist/providers/simple-options.d.ts +0 -8
- package/dist/providers/simple-options.d.ts.map +0 -1
- package/dist/providers/simple-options.js +0 -36
- package/dist/providers/simple-options.js.map +0 -1
- package/dist/providers/transform-messages.d.ts.map +0 -1
- package/dist/providers/transform-messages.js.map +0 -1
- package/dist/stream.d.ts +0 -8
- package/dist/stream.d.ts.map +0 -1
- package/dist/stream.js +0 -27
- package/dist/stream.js.map +0 -1
- package/dist/utils/oauth/anthropic.d.ts +0 -25
- package/dist/utils/oauth/anthropic.d.ts.map +0 -1
- package/dist/utils/oauth/anthropic.js.map +0 -1
- package/dist/utils/oauth/github-copilot.d.ts +0 -30
- package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
- package/dist/utils/oauth/github-copilot.js +0 -292
- package/dist/utils/oauth/github-copilot.js.map +0 -1
- package/dist/utils/oauth/google-antigravity.d.ts +0 -26
- package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
- package/dist/utils/oauth/google-antigravity.js +0 -377
- package/dist/utils/oauth/google-antigravity.js.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
- package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.js +0 -482
- package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
- package/dist/utils/oauth/index.d.ts +0 -61
- package/dist/utils/oauth/index.d.ts.map +0 -1
- package/dist/utils/oauth/index.js +0 -131
- package/dist/utils/oauth/index.js.map +0 -1
- package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
- package/dist/utils/oauth/oauth-page.js.map +0 -1
- package/dist/utils/oauth/openai-codex.d.ts +0 -34
- package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
- package/dist/utils/oauth/openai-codex.js +0 -374
- package/dist/utils/oauth/openai-codex.js.map +0 -1
- package/dist/utils/oauth/pkce.d.ts.map +0 -1
- package/dist/utils/oauth/pkce.js.map +0 -1
- package/dist/utils/oauth/types.d.ts +0 -47
- package/dist/utils/oauth/types.d.ts.map +0 -1
- package/dist/utils/oauth/types.js.map +0 -1
- /package/dist/{providers → api}/github-copilot-headers.js +0 -0
- /package/dist/{utils → auth}/oauth/oauth-page.d.ts +0 -0
- /package/dist/{utils → auth}/oauth/oauth-page.js +0 -0
- /package/dist/{utils → auth}/oauth/pkce.d.ts +0 -0
- /package/dist/{utils → auth}/oauth/pkce.js +0 -0
- /package/dist/{utils/oauth → auth}/types.js +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @sammorrowdrums/mcpi-ai
|
|
2
2
|
|
|
3
|
-
Unified LLM API with
|
|
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
5
|
**Note**: This library only includes models that support tool calling (function calling), as this is essential for agentic workflows.
|
|
6
6
|
|
|
@@ -9,6 +9,17 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
9
9
|
- [Supported Providers](#supported-providers)
|
|
10
10
|
- [Installation](#installation)
|
|
11
11
|
- [Quick Start](#quick-start)
|
|
12
|
+
- [Providers and Models](#providers-and-models)
|
|
13
|
+
- [Provider Factories](#provider-factories)
|
|
14
|
+
- [All Built-in Providers](#all-built-in-providers)
|
|
15
|
+
- [Querying Models](#querying-models)
|
|
16
|
+
- [Static Catalog Reads](#static-catalog-reads)
|
|
17
|
+
- [Dynamic Providers](#dynamic-providers)
|
|
18
|
+
- [Auth](#auth)
|
|
19
|
+
- [How Auth Resolves](#how-auth-resolves)
|
|
20
|
+
- [Transforming Request Headers](#transforming-request-headers)
|
|
21
|
+
- [Credential Store](#credential-store)
|
|
22
|
+
- [Environment Variables](#environment-variables)
|
|
12
23
|
- [Tools](#tools)
|
|
13
24
|
- [Defining Tools](#defining-tools)
|
|
14
25
|
- [Handling Tool Calls](#handling-tool-calls)
|
|
@@ -16,6 +27,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
16
27
|
- [Validating Tool Arguments](#validating-tool-arguments)
|
|
17
28
|
- [Complete Event Reference](#complete-event-reference)
|
|
18
29
|
- [Image Input](#image-input)
|
|
30
|
+
- [Image Generation](#image-generation)
|
|
19
31
|
- [Thinking/Reasoning](#thinkingreasoning)
|
|
20
32
|
- [Unified Interface](#unified-interface-streamsimplecompletesimple)
|
|
21
33
|
- [Provider-Specific Options](#provider-specific-options-streamcomplete)
|
|
@@ -24,67 +36,80 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
24
36
|
- [Error Handling](#error-handling)
|
|
25
37
|
- [Aborting Requests](#aborting-requests)
|
|
26
38
|
- [Continuing After Abort](#continuing-after-abort)
|
|
27
|
-
- [
|
|
28
|
-
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
39
|
+
- [Debugging Provider Payloads](#debugging-provider-payloads)
|
|
40
|
+
- [Custom Providers](#custom-providers)
|
|
41
|
+
- [createProvider()](#createprovider)
|
|
42
|
+
- [Calling API Implementations Directly](#calling-api-implementations-directly)
|
|
31
43
|
- [OpenAI Compatibility Settings](#openai-compatibility-settings)
|
|
32
|
-
|
|
44
|
+
- [Faux Provider for Tests](#faux-provider-for-tests)
|
|
33
45
|
- [Cross-Provider Handoffs](#cross-provider-handoffs)
|
|
34
46
|
- [Context Serialization](#context-serialization)
|
|
35
47
|
- [Browser Usage](#browser-usage)
|
|
36
|
-
|
|
37
|
-
- [Environment Variables](#environment-variables-nodejs-only)
|
|
38
|
-
- [Checking Environment Variables](#checking-environment-variables)
|
|
48
|
+
- [Bundling and Tree Shaking](#bundling-and-tree-shaking)
|
|
39
49
|
- [OAuth Providers](#oauth-providers)
|
|
40
50
|
- [Vertex AI](#vertex-ai)
|
|
41
51
|
- [CLI Login](#cli-login)
|
|
42
52
|
- [Programmatic OAuth](#programmatic-oauth)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- [Provider Notes](#provider-notes)
|
|
53
|
+
- [Migrating from the Old Global API](#migrating-from-the-old-global-api)
|
|
54
|
+
- [Development](#development)
|
|
46
55
|
- [License](#license)
|
|
47
56
|
|
|
48
57
|
## Supported Providers
|
|
49
58
|
|
|
50
59
|
- **OpenAI**
|
|
60
|
+
- **Ant Ling**
|
|
51
61
|
- **Azure OpenAI (Responses)**
|
|
52
62
|
- **OpenAI Codex** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
|
|
63
|
+
- **DeepSeek**
|
|
64
|
+
- **NVIDIA NIM**
|
|
53
65
|
- **Anthropic**
|
|
54
66
|
- **Google**
|
|
55
67
|
- **Vertex AI** (Gemini via Vertex AI)
|
|
56
68
|
- **Mistral**
|
|
57
69
|
- **Groq**
|
|
58
70
|
- **Cerebras**
|
|
71
|
+
- **Cloudflare AI Gateway**
|
|
72
|
+
- **Cloudflare Workers AI**
|
|
59
73
|
- **xAI**
|
|
60
74
|
- **OpenRouter**
|
|
61
75
|
- **Vercel AI Gateway**
|
|
62
|
-
- **
|
|
76
|
+
- **ZAI Coding Plan (Global)** (with separate China provider)
|
|
77
|
+
- **MiniMax** (with separate China provider)
|
|
78
|
+
- **Together AI**
|
|
79
|
+
- **Baseten**
|
|
80
|
+
- **Hugging Face**
|
|
81
|
+
- **Moonshot AI** (with separate China provider)
|
|
63
82
|
- **GitHub Copilot** (requires OAuth, see below)
|
|
64
|
-
- **Google Gemini CLI** (requires OAuth, see below)
|
|
65
|
-
- **Antigravity** (requires OAuth, see below)
|
|
66
83
|
- **Amazon Bedrock**
|
|
67
84
|
- **OpenCode Zen**
|
|
68
85
|
- **OpenCode Go**
|
|
69
|
-
- **Fireworks** (uses Anthropic-compatible
|
|
70
|
-
- **Kimi For Coding** (Moonshot AI, uses Anthropic-compatible API)
|
|
86
|
+
- **Fireworks** (uses OpenAI- and Anthropic-compatible APIs)
|
|
87
|
+
- **Kimi For Coding** (Moonshot AI subscription endpoint, uses Anthropic-compatible API)
|
|
88
|
+
- **Qwen Token Plan** (separate Individual and existing catalogs, with a separate China provider)
|
|
89
|
+
- **Xiaomi MiMo** (defaults to API billing endpoint, with separate Token Plan providers for `cn`/`ams`/`sgp` regions)
|
|
71
90
|
- **Any OpenAI-compatible API**: Ollama, vLLM, LM Studio, etc.
|
|
72
91
|
|
|
73
92
|
## Installation
|
|
74
93
|
|
|
75
94
|
```bash
|
|
76
|
-
npm install @
|
|
95
|
+
npm install @sammorrowdrums/mcpi-ai
|
|
77
96
|
```
|
|
78
97
|
|
|
79
|
-
TypeBox exports are re-exported from `@
|
|
98
|
+
TypeBox exports are re-exported from `@sammorrowdrums/mcpi-ai`: `Type`, `Static`, and `TSchema`.
|
|
80
99
|
|
|
81
100
|
## Quick Start
|
|
82
101
|
|
|
102
|
+
You build a `Models` collection of providers and stream through it. The quickest start registers every built-in provider; apps that care about bundle size register individual providers instead (see [Provider Factories](#provider-factories) and [Bundling and Tree Shaking](#bundling-and-tree-shaking)).
|
|
103
|
+
|
|
83
104
|
```typescript
|
|
84
|
-
import { Type,
|
|
105
|
+
import { Type, type Context, type Tool } from '@sammorrowdrums/mcpi-ai';
|
|
106
|
+
import { builtinModels } from '@sammorrowdrums/mcpi-ai/providers/all';
|
|
107
|
+
|
|
108
|
+
// A Models collection with every built-in provider registered
|
|
109
|
+
const models = builtinModels();
|
|
85
110
|
|
|
86
|
-
//
|
|
87
|
-
const model = getModel('openai', 'gpt-4o-mini')
|
|
111
|
+
// Sync lookup against the collection
|
|
112
|
+
const model = models.getModel('openai', 'gpt-4o-mini')!;
|
|
88
113
|
|
|
89
114
|
// Define tools with TypeBox schemas for type safety and validation
|
|
90
115
|
const tools: Tool[] = [{
|
|
@@ -98,12 +123,13 @@ const tools: Tool[] = [{
|
|
|
98
123
|
// Build a conversation context (easily serializable and transferable between models)
|
|
99
124
|
const context: Context = {
|
|
100
125
|
systemPrompt: 'You are a helpful assistant.',
|
|
101
|
-
messages: [{ role: 'user', content: 'What time is it?' }],
|
|
126
|
+
messages: [{ role: 'user', content: 'What time is it?', timestamp: Date.now() }],
|
|
102
127
|
tools
|
|
103
128
|
};
|
|
104
129
|
|
|
105
|
-
// Option 1: Streaming with all event types
|
|
106
|
-
|
|
130
|
+
// Option 1: Streaming with all event types.
|
|
131
|
+
// Auth resolves through the provider (OPENAI_API_KEY from the environment here).
|
|
132
|
+
const s = models.stream(model, context);
|
|
107
133
|
|
|
108
134
|
for await (const event of s) {
|
|
109
135
|
switch (event.type) {
|
|
@@ -146,7 +172,7 @@ for await (const event of s) {
|
|
|
146
172
|
console.log(`\nFinished: ${event.reason}`);
|
|
147
173
|
break;
|
|
148
174
|
case 'error':
|
|
149
|
-
console.error(`Error: ${event.error}`);
|
|
175
|
+
console.error(`Error: ${event.error.errorMessage}`);
|
|
150
176
|
break;
|
|
151
177
|
}
|
|
152
178
|
}
|
|
@@ -158,7 +184,6 @@ context.messages.push(finalMessage);
|
|
|
158
184
|
// Handle tool calls if any
|
|
159
185
|
const toolCalls = finalMessage.content.filter(b => b.type === 'toolCall');
|
|
160
186
|
for (const call of toolCalls) {
|
|
161
|
-
// Execute the tool
|
|
162
187
|
const result = call.name === 'get_time'
|
|
163
188
|
? new Date().toLocaleString('en-US', {
|
|
164
189
|
timeZone: call.arguments.timezone || 'UTC',
|
|
@@ -180,7 +205,7 @@ for (const call of toolCalls) {
|
|
|
180
205
|
|
|
181
206
|
// Continue if there were tool calls
|
|
182
207
|
if (toolCalls.length > 0) {
|
|
183
|
-
const continuation = await complete(model, context);
|
|
208
|
+
const continuation = await models.complete(model, context);
|
|
184
209
|
context.messages.push(continuation);
|
|
185
210
|
console.log('After tool execution:', continuation.content);
|
|
186
211
|
}
|
|
@@ -189,7 +214,7 @@ console.log(`Total tokens: ${finalMessage.usage.input} in, ${finalMessage.usage.
|
|
|
189
214
|
console.log(`Cost: $${finalMessage.usage.cost.total.toFixed(4)}`);
|
|
190
215
|
|
|
191
216
|
// Option 2: Get complete response without streaming
|
|
192
|
-
const response = await complete(model, context);
|
|
217
|
+
const response = await models.complete(model, context);
|
|
193
218
|
|
|
194
219
|
for (const block of response.content) {
|
|
195
220
|
if (block.type === 'text') {
|
|
@@ -200,6 +225,236 @@ for (const block of response.content) {
|
|
|
200
225
|
}
|
|
201
226
|
```
|
|
202
227
|
|
|
228
|
+
Snippets in the rest of this README assume a `models` collection set up like this (with the relevant providers registered).
|
|
229
|
+
|
|
230
|
+
## Providers and Models
|
|
231
|
+
|
|
232
|
+
A **provider** is the runtime unit: it owns its model catalog, its auth (API key resolution, OAuth flows), and its stream behavior. A `Models` collection holds providers and routes every request to the provider that owns the model.
|
|
233
|
+
|
|
234
|
+
Providers internally share **API implementations** (the wire protocols): Anthropic models use `anthropic-messages`, OpenAI uses `openai-responses`, while xAI, Groq, Cerebras, OpenRouter, and most others share `openai-completions`. Mixed-API providers (GitHub Copilot, OpenCode Zen) dispatch per model.
|
|
235
|
+
|
|
236
|
+
### Provider Factories
|
|
237
|
+
|
|
238
|
+
For apps that only need specific providers, there is one factory per built-in provider, each a subpath import that pulls only that provider's catalog:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
|
|
242
|
+
import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
|
|
243
|
+
import { openrouterProvider } from '@sammorrowdrums/mcpi-ai/providers/openrouter';
|
|
244
|
+
import { amazonBedrockProvider } from '@sammorrowdrums/mcpi-ai/providers/amazon-bedrock';
|
|
245
|
+
// ...one module per provider in the Supported Providers list
|
|
246
|
+
|
|
247
|
+
const models = createModels();
|
|
248
|
+
models.setProvider(anthropicProvider());
|
|
249
|
+
models.setProvider(openrouterProvider());
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Provider factories import their model catalog and a lazy API wrapper. They do not import other providers. With bundler code splitting, SDK implementations (`@anthropic-ai/sdk`, `openai`, `@google/genai`, etc.) stay in lazy chunks loaded on the first request to a model of that API.
|
|
253
|
+
|
|
254
|
+
### All Built-in Providers
|
|
255
|
+
|
|
256
|
+
For apps that want everything (as in Quick Start):
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { builtinModels } from '@sammorrowdrums/mcpi-ai/providers/all';
|
|
260
|
+
|
|
261
|
+
const models = builtinModels(); // a Models collection with every built-in provider registered
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
This imports all catalogs and every built-in provider factory. It is the heavy, explicit entrypoint. `builtinModels()` accepts the same options as `createModels()` (`credentials`, `authContext`); `builtinProviders()` returns the provider array if you want to register them on your own collection.
|
|
265
|
+
|
|
266
|
+
### Querying Models
|
|
267
|
+
|
|
268
|
+
Reads are synchronous and return the last-known lists:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const providers = models.getProviders(); // registered Provider objects
|
|
272
|
+
const provider = models.getProvider('anthropic'); // one provider
|
|
273
|
+
|
|
274
|
+
const all = models.getModels(); // every model across providers
|
|
275
|
+
const anthropicModels = models.getModels('anthropic');
|
|
276
|
+
const model = models.getModel('anthropic', 'claude-sonnet-4-5');
|
|
277
|
+
|
|
278
|
+
for (const m of anthropicModels) {
|
|
279
|
+
console.log(`${m.id}: ${m.name}`);
|
|
280
|
+
console.log(` API: ${m.api}`);
|
|
281
|
+
console.log(` Context: ${m.contextWindow} tokens`);
|
|
282
|
+
console.log(` Vision: ${m.input.includes('image')}`);
|
|
283
|
+
console.log(` Reasoning: ${m.reasoning}`);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Dynamically listed models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import { hasApi } from '@sammorrowdrums/mcpi-ai';
|
|
291
|
+
|
|
292
|
+
const m = models.getModel('anthropic', 'claude-sonnet-4-5');
|
|
293
|
+
if (m && hasApi(m, 'anthropic-messages')) {
|
|
294
|
+
// m: Model<'anthropic-messages'> — stream options fully typed
|
|
295
|
+
models.stream(m, context, { thinkingEnabled: true, thinkingBudgetTokens: 2048 });
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Static Catalog Reads
|
|
300
|
+
|
|
301
|
+
For tooling that wants the generated built-in catalog with full literal typing (provider and model IDs auto-complete), independent of any collection:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from '@sammorrowdrums/mcpi-ai/providers/all';
|
|
305
|
+
|
|
306
|
+
const model = getBuiltinModel('openai', 'gpt-4o-mini'); // typed Model<'openai-responses'>
|
|
307
|
+
const providers = getBuiltinProviders();
|
|
308
|
+
const anthropic = getBuiltinModels('anthropic');
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Dynamic Providers
|
|
312
|
+
|
|
313
|
+
Providers may have dynamic model lists (a llama.cpp server, a live OpenRouter listing). Reads stay sync; fetching is an explicit async verb:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// getModels() returns the last-known list (empty before the first refresh)
|
|
317
|
+
await models.refresh({ providers: ['llamacpp'] }); // refresh one provider
|
|
318
|
+
await models.refresh(); // refresh all providers concurrently, best-effort
|
|
319
|
+
const fresh = models.getModel('llamacpp', 'qwen3-30b');
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Static built-in providers are no-ops for `refresh()`. See [createProvider()](#createprovider) for building a dynamic provider.
|
|
323
|
+
|
|
324
|
+
## Auth
|
|
325
|
+
|
|
326
|
+
Every provider owns its auth: how API keys resolve (stored credentials, environment variables, ambient sources like AWS profiles or gcloud ADC) and, where supported, OAuth login/refresh flows.
|
|
327
|
+
|
|
328
|
+
### How Auth Resolves
|
|
329
|
+
|
|
330
|
+
When you call `models.stream()`, the collection resolves auth through the owning provider and merges it into the request. Explicit per-request values always win:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// Resolved through the provider (env var, stored credential, OAuth token):
|
|
334
|
+
await models.complete(model, context);
|
|
335
|
+
|
|
336
|
+
// Explicit key wins over anything the provider would resolve:
|
|
337
|
+
await models.complete(model, context, { apiKey: 'sk-explicit' });
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
You can inspect resolution without making a request. Pass a provider ID for provider-scoped auth, or a model to include its static `model.headers`:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const providerAuth = await models.getAuth(model.provider);
|
|
344
|
+
const modelAuth = await models.getAuth(model);
|
|
345
|
+
|
|
346
|
+
if (modelAuth) {
|
|
347
|
+
console.log(`configured via ${modelAuth.source}`); // e.g. "ANTHROPIC_API_KEY", "OAuth", "stored credential"
|
|
348
|
+
console.log(modelAuth.auth.headers); // Provider auth headers + model.headers
|
|
349
|
+
} else {
|
|
350
|
+
console.log('not configured');
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
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.
|
|
355
|
+
|
|
356
|
+
`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.
|
|
357
|
+
|
|
358
|
+
### Transforming Request Headers
|
|
359
|
+
|
|
360
|
+
`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:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
const response = await models.completeSimple(model, context, {
|
|
364
|
+
headers: { "X-Client": "my-app" },
|
|
365
|
+
transformHeaders: async (headers) => ({
|
|
366
|
+
...headers,
|
|
367
|
+
"X-Request-ID": crypto.randomUUID(),
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The ordering is:
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
provider auth headers -> model.headers -> explicit options.headers -> transformHeaders -> Provider.stream*()
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Header names are merged case-insensitively. Explicit headers override auth/model headers, and the transform has final control; returning `null` for a header suppresses lower-level defaults that support deletion.
|
|
379
|
+
|
|
380
|
+
`transformHeaders` belongs to `Models`, not `Provider`. A `Models` implementation must consume it and remove it before calling `Provider.stream*()`. Provider implementations continue receiving ordinary `ApiStreamOptions` or `SimpleStreamOptions` and never handle the transform themselves. Use this option instead of calling `getAuth(model)` before `stream*()`, which would resolve request auth twice.
|
|
381
|
+
|
|
382
|
+
### Credential Store
|
|
383
|
+
|
|
384
|
+
Stored credentials (API keys entered interactively, OAuth tokens) live in a `CredentialStore` — one type-tagged credential per provider. mcpi-ai ships an in-memory default; apps inject persistent storage:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { createModels, type CredentialStore } from '@sammorrowdrums/mcpi-ai';
|
|
388
|
+
|
|
389
|
+
const models = createModels({ credentials: myFileBackedStore });
|
|
390
|
+
// builtinModels() takes the same options:
|
|
391
|
+
// const models = builtinModels({ credentials: myFileBackedStore });
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
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.
|
|
395
|
+
|
|
396
|
+
API-key credentials use the same discriminator as mcpi's `auth.json` and can carry provider-scoped env/config values:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
const credential = {
|
|
400
|
+
type: 'api_key',
|
|
401
|
+
key: '...',
|
|
402
|
+
env: {
|
|
403
|
+
CLOUDFLARE_ACCOUNT_ID: 'account-id',
|
|
404
|
+
CLOUDFLARE_GATEWAY_ID: 'gateway-id'
|
|
405
|
+
}
|
|
406
|
+
} as const;
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Environment Variables
|
|
410
|
+
|
|
411
|
+
Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` explicitly):
|
|
412
|
+
|
|
413
|
+
| Provider | Environment Variable(s) |
|
|
414
|
+
|----------|------------------------|
|
|
415
|
+
| OpenAI | `OPENAI_API_KEY` |
|
|
416
|
+
| Ant Ling | `ANT_LING_API_KEY` |
|
|
417
|
+
| 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`. |
|
|
418
|
+
| Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
|
|
419
|
+
| DeepSeek | `DEEPSEEK_API_KEY` |
|
|
420
|
+
| NVIDIA NIM | `NVIDIA_API_KEY` |
|
|
421
|
+
| Google | `GEMINI_API_KEY` |
|
|
422
|
+
| Vertex AI | `GOOGLE_CLOUD_API_KEY` or `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) + `GOOGLE_CLOUD_LOCATION` + ADC |
|
|
423
|
+
| Mistral | `MISTRAL_API_KEY` |
|
|
424
|
+
| Groq | `GROQ_API_KEY` |
|
|
425
|
+
| Cerebras | `CEREBRAS_API_KEY` |
|
|
426
|
+
| Cloudflare AI Gateway | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
|
|
427
|
+
| Cloudflare Workers AI | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` |
|
|
428
|
+
| xAI | `XAI_API_KEY` |
|
|
429
|
+
| Fireworks | `FIREWORKS_API_KEY` |
|
|
430
|
+
| Together AI | `TOGETHER_API_KEY` |
|
|
431
|
+
| Baseten | `BASETEN_API_KEY` |
|
|
432
|
+
| OpenRouter | `OPENROUTER_API_KEY` |
|
|
433
|
+
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
|
|
434
|
+
| ZAI Coding Plan (Global) | `ZAI_API_KEY` |
|
|
435
|
+
| ZAI Coding Plan (China) | `ZAI_CODING_CN_API_KEY` |
|
|
436
|
+
| MiniMax (Global) | `MINIMAX_API_KEY` |
|
|
437
|
+
| MiniMax (China) | `MINIMAX_CN_API_KEY` |
|
|
438
|
+
| Moonshot AI / Moonshot AI (China) | `MOONSHOT_API_KEY` |
|
|
439
|
+
| Hugging Face | `HF_TOKEN` |
|
|
440
|
+
| OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
|
|
441
|
+
| Kimi For Coding | `KIMI_API_KEY` |
|
|
442
|
+
| Qwen Token Plan (existing catalog) | `QWEN_TOKEN_PLAN_API_KEY` |
|
|
443
|
+
| Qwen Token Plan (Individual) | `QWEN_TOKEN_PLAN_API_KEY` |
|
|
444
|
+
| Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` |
|
|
445
|
+
| Xiaomi MiMo (API billing) | `XIAOMI_API_KEY` |
|
|
446
|
+
| Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` |
|
|
447
|
+
| Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` |
|
|
448
|
+
| Xiaomi MiMo Token Plan (Singapore) | `XIAOMI_TOKEN_PLAN_SGP_API_KEY` |
|
|
449
|
+
| GitHub Copilot | `COPILOT_GITHUB_TOKEN` |
|
|
450
|
+
|
|
451
|
+
`qwen-token-plan-individual` and `qwen-token-plan` share the international endpoint and
|
|
452
|
+
`QWEN_TOKEN_PLAN_API_KEY`. The Individual provider exposes only the models documented for Individual
|
|
453
|
+
subscriptions, while the existing provider retains its broader catalog for backward compatibility.
|
|
454
|
+
Stored credentials remain provider-scoped, so save the key under the provider ID you register.
|
|
455
|
+
|
|
456
|
+
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.
|
|
457
|
+
|
|
203
458
|
## Tools
|
|
204
459
|
|
|
205
460
|
Tools enable LLMs to interact with external systems. This library uses TypeBox schemas for type-safe tool definitions with automatic validation using TypeBox's built-in validator and value conversion utilities. TypeBox schemas can be serialized and deserialized as plain JSON, making them ideal for distributed systems.
|
|
@@ -207,7 +462,7 @@ Tools enable LLMs to interact with external systems. This library uses TypeBox s
|
|
|
207
462
|
### Defining Tools
|
|
208
463
|
|
|
209
464
|
```typescript
|
|
210
|
-
import { Type, Tool, StringEnum } from '@
|
|
465
|
+
import { Type, type Tool, StringEnum } from '@sammorrowdrums/mcpi-ai';
|
|
211
466
|
|
|
212
467
|
// Define tool parameters with TypeBox
|
|
213
468
|
const weatherTool: Tool = {
|
|
@@ -234,6 +489,40 @@ const bookMeetingTool: Tool = {
|
|
|
234
489
|
};
|
|
235
490
|
```
|
|
236
491
|
|
|
492
|
+
### Constrained Sampling for Tools
|
|
493
|
+
|
|
494
|
+
Tools can opt in to provider-side constrained sampling. For JSON-schema tools, `strict: 'prefer'` uses provider-side strict schema enforcement when supported and otherwise falls back to normal tool calling. `strict: 'require'` fails the request when the active provider/model cannot honor it. Set `constrainedSampling: false` to explicitly opt out; it behaves the same as omitting the field.
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
const strictTool: Tool = {
|
|
498
|
+
name: 'edit_file',
|
|
499
|
+
description: 'Edit a file',
|
|
500
|
+
parameters: Type.Object({
|
|
501
|
+
path: Type.String(),
|
|
502
|
+
content: Type.String()
|
|
503
|
+
}, { additionalProperties: false }),
|
|
504
|
+
constrainedSampling: { type: 'json_schema', strict: 'prefer' }
|
|
505
|
+
};
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Strict JSON-schema constrained sampling is supported for OpenAI, Anthropic, supported Amazon Bedrock Converse models, Mistral, and Gemini 3 tool calls through the Google Generative AI and Vertex adapters. Google uses `VALIDATED` function-calling mode (or `ANY` when explicitly requested); earlier Gemini versions fall back for `strict: 'prefer'` and reject `strict: 'require'` because they do not enforce required parameters. Bedrock strict-tool capability is generated from model structured-output metadata; custom Bedrock models can override `compat.supportsStrictMode`. OpenAI Responses and Chat Completions can also emit grammar-constrained custom tools with OpenAI Lark or regex grammar variants. If multiple OpenAI variants are supplied, Lark is preferred over regex. Grammar constraints are enforced when the active model supports grammar tools; otherwise the tool falls back to normal function/JSON-schema handling. Grammar tool capability is model metadata: the generated catalog sets `compat.supportsOpenAIGrammarTools` for GPT-5+ models on endpoints that pass OpenAI custom tools through (OpenAI, OpenAI Codex, Azure OpenAI Responses, GitHub Copilot, opencode, and Cloudflare AI Gateway). OpenAI rejects `type: "custom"` tools for pre-GPT-5 models, and gateways that normalize tool schemas (e.g. OpenRouter) mangle them, so the flag stays off elsewhere. Custom model definitions can opt in via `compat`. Grammar-capable models reject grammar configurations without a non-empty supported variant. Native grammar tools must have an object parameter schema with exactly one required string property:
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
const patchTool: Tool = {
|
|
512
|
+
name: 'apply_patch',
|
|
513
|
+
description: 'Apply a patch',
|
|
514
|
+
parameters: Type.Object({
|
|
515
|
+
input: Type.String()
|
|
516
|
+
}, { additionalProperties: false }),
|
|
517
|
+
constrainedSampling: {
|
|
518
|
+
type: 'grammar',
|
|
519
|
+
variants: {
|
|
520
|
+
openai_lark: 'start: /.+/s'
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
```
|
|
525
|
+
|
|
237
526
|
### Handling Tool Calls
|
|
238
527
|
|
|
239
528
|
Tool results use content blocks and can include both text and images:
|
|
@@ -242,11 +531,11 @@ Tool results use content blocks and can include both text and images:
|
|
|
242
531
|
import { readFileSync } from 'fs';
|
|
243
532
|
|
|
244
533
|
const context: Context = {
|
|
245
|
-
messages: [{ role: 'user', content: 'What is the weather in London?' }],
|
|
534
|
+
messages: [{ role: 'user', content: 'What is the weather in London?', timestamp: Date.now() }],
|
|
246
535
|
tools: [weatherTool]
|
|
247
536
|
};
|
|
248
537
|
|
|
249
|
-
const response = await complete(model, context);
|
|
538
|
+
const response = await models.complete(model, context);
|
|
250
539
|
|
|
251
540
|
// Check for tool calls in the response
|
|
252
541
|
for (const block of response.content) {
|
|
@@ -287,7 +576,7 @@ context.messages.push({
|
|
|
287
576
|
During streaming, tool call arguments are progressively parsed as they arrive. This enables real-time UI updates before the complete arguments are available:
|
|
288
577
|
|
|
289
578
|
```typescript
|
|
290
|
-
const s = stream(model, context);
|
|
579
|
+
const s = models.stream(model, context);
|
|
291
580
|
|
|
292
581
|
for await (const event of s) {
|
|
293
582
|
if (event.type === 'toolcall_delta') {
|
|
@@ -328,15 +617,13 @@ for await (const event of s) {
|
|
|
328
617
|
|
|
329
618
|
### Validating Tool Arguments
|
|
330
619
|
|
|
331
|
-
When
|
|
332
|
-
|
|
333
|
-
When implementing your own tool execution loop with `stream()` or `complete()`, use `validateToolCall` to validate arguments before passing them to your tools:
|
|
620
|
+
When implementing your own tool execution loop, use `validateToolCall` to validate arguments before passing them to your tools:
|
|
334
621
|
|
|
335
622
|
```typescript
|
|
336
|
-
import {
|
|
623
|
+
import { validateToolCall, type Tool } from '@sammorrowdrums/mcpi-ai';
|
|
337
624
|
|
|
338
625
|
const tools: Tool[] = [weatherTool, calculatorTool];
|
|
339
|
-
const s = stream(model, { messages, tools });
|
|
626
|
+
const s = models.stream(model, { messages, tools });
|
|
340
627
|
|
|
341
628
|
for await (const event of s) {
|
|
342
629
|
if (event.type === 'toolcall_end') {
|
|
@@ -381,15 +668,16 @@ All streaming events emitted during assistant message generation:
|
|
|
381
668
|
| `done` | Stream complete | `reason`: Stop reason ("stop", "length", "toolUse"), `message`: Final assistant message |
|
|
382
669
|
| `error` | Error occurred | `reason`: Error type ("error" or "aborted"), `error`: AssistantMessage with partial content |
|
|
383
670
|
|
|
671
|
+
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 mcpi 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.
|
|
672
|
+
|
|
384
673
|
## Image Input
|
|
385
674
|
|
|
386
675
|
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.
|
|
387
676
|
|
|
388
677
|
```typescript
|
|
389
678
|
import { readFileSync } from 'fs';
|
|
390
|
-
import { getModel, complete } from '@mariozechner/pi-ai';
|
|
391
679
|
|
|
392
|
-
const model = getModel('openai', 'gpt-4o-mini')
|
|
680
|
+
const model = models.getModel('openai', 'gpt-4o-mini')!;
|
|
393
681
|
|
|
394
682
|
// Check if model supports images
|
|
395
683
|
if (model.input.includes('image')) {
|
|
@@ -399,13 +687,14 @@ if (model.input.includes('image')) {
|
|
|
399
687
|
const imageBuffer = readFileSync('image.png');
|
|
400
688
|
const base64Image = imageBuffer.toString('base64');
|
|
401
689
|
|
|
402
|
-
const response = await complete(model, {
|
|
690
|
+
const response = await models.complete(model, {
|
|
403
691
|
messages: [{
|
|
404
692
|
role: 'user',
|
|
405
693
|
content: [
|
|
406
694
|
{ type: 'text', text: 'What is in this image?' },
|
|
407
695
|
{ type: 'image', data: base64Image, mimeType: 'image/png' }
|
|
408
|
-
]
|
|
696
|
+
],
|
|
697
|
+
timestamp: Date.now()
|
|
409
698
|
}]
|
|
410
699
|
});
|
|
411
700
|
|
|
@@ -417,6 +706,83 @@ for (const block of response.content) {
|
|
|
417
706
|
}
|
|
418
707
|
```
|
|
419
708
|
|
|
709
|
+
## Image Generation
|
|
710
|
+
|
|
711
|
+
Image generation uses a separate API surface from text/chat generation, mirroring the chat-side design: an `ImagesModels` collection holds `ImagesProvider`s, reads are sync, and auth resolves through the owning provider. Image generation is a one-shot API: `generateImages()` waits for the provider response and returns the final `AssistantImages` result — do not use the chat/stream APIs for it.
|
|
712
|
+
|
|
713
|
+
### Basic Image Generation
|
|
714
|
+
|
|
715
|
+
```typescript
|
|
716
|
+
import { builtinImagesModels } from '@sammorrowdrums/mcpi-ai/providers/all';
|
|
717
|
+
|
|
718
|
+
// Every built-in image-generation provider; accepts the same options as createModels()
|
|
719
|
+
const imagesModels = builtinImagesModels();
|
|
720
|
+
|
|
721
|
+
const model = imagesModels.getModel('openrouter', 'google/gemini-2.5-flash-image')!;
|
|
722
|
+
|
|
723
|
+
// Auth resolves through the provider (OPENROUTER_API_KEY here); explicit apiKey wins
|
|
724
|
+
const result = await imagesModels.generateImages(model, {
|
|
725
|
+
input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
for (const block of result.output) {
|
|
729
|
+
if (block.type === 'text') {
|
|
730
|
+
console.log(block.text);
|
|
731
|
+
} else if (block.type === 'image') {
|
|
732
|
+
console.log(block.mimeType);
|
|
733
|
+
console.log(block.data.substring(0, 32));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Like the chat side, you can build the collection from parts: `createImagesModels({ credentials?, authContext? })`, the `openrouterImagesProvider()` factory from `@sammorrowdrums/mcpi-ai/providers/openrouter-images`, and `createImagesProvider({ id, auth, models, refreshModels?, api })` for custom image providers (with `imagesModels.refresh(provider?)` for dynamic lists). Failures never reject — they return an `AssistantImages` with `stopReason: "error"`. The collection's provider-scoped `getAuth(providerId)` works exactly like the chat-side one.
|
|
739
|
+
|
|
740
|
+
The old global API (`getImageModel()` / `getImageModels()` / `getImageProviders()` / `generateImages()`) remains available on the [compat entrypoint](#migrating-from-the-old-global-api):
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
import { getImageModel, generateImages } from '@sammorrowdrums/mcpi-ai/compat';
|
|
744
|
+
|
|
745
|
+
const model = getImageModel('openrouter', 'google/gemini-2.5-flash-image');
|
|
746
|
+
const result = await generateImages(model, {
|
|
747
|
+
input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
|
|
748
|
+
}, {
|
|
749
|
+
apiKey: process.env.OPENROUTER_API_KEY
|
|
750
|
+
});
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
Some models also support image input:
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
import { readFileSync } from 'fs';
|
|
757
|
+
|
|
758
|
+
const imageBuffer = readFileSync('input.png');
|
|
759
|
+
const result = await imagesModels.generateImages(model, {
|
|
760
|
+
input: [
|
|
761
|
+
{ type: 'text', text: 'Create a variation of this image with a blue background.' },
|
|
762
|
+
{ type: 'image', data: imageBuffer.toString('base64'), mimeType: 'image/png' }
|
|
763
|
+
]
|
|
764
|
+
});
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Check capabilities on the model metadata:
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
console.log(model.input); // ['text', 'image']
|
|
771
|
+
console.log(model.output); // ['image'] or ['image', 'text']
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### Notes and Limitations
|
|
775
|
+
|
|
776
|
+
- Image models live in `ImagesModels` collections, chat models in `Models` collections; the two are separate surfaces.
|
|
777
|
+
- Use `generateImages()`, not the chat/stream APIs.
|
|
778
|
+
- Image-generation models do not participate in tool calling.
|
|
779
|
+
- Outputs are returned in `AssistantImages.output` and can include both base64-encoded `ImageContent` blocks and `TextContent` blocks.
|
|
780
|
+
- Some models return only images, others return images plus text. Check `model.output`.
|
|
781
|
+
- Some models accept image input, others are text-to-image only. Check `model.input`.
|
|
782
|
+
- Like the streaming APIs, image generation supports options such as `apiKey`, `signal`, `headers`, `onPayload`, and `onResponse`, and results may include `stopReason`, `responseId`, and `usage`.
|
|
783
|
+
- 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.
|
|
784
|
+
- At the moment, image generation is available through only one provider, OpenRouter.
|
|
785
|
+
|
|
420
786
|
## Thinking/Reasoning
|
|
421
787
|
|
|
422
788
|
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.
|
|
@@ -424,16 +790,11 @@ Many models support thinking/reasoning capabilities where they can show their in
|
|
|
424
790
|
### Unified Interface (streamSimple/completeSimple)
|
|
425
791
|
|
|
426
792
|
```typescript
|
|
427
|
-
import { getModel, streamSimple, completeSimple } from '@mariozechner/pi-ai';
|
|
428
|
-
|
|
429
793
|
// Many models across providers support thinking/reasoning
|
|
430
|
-
const model = getModel('anthropic', 'claude-sonnet-4-
|
|
431
|
-
// or getModel('openai', 'gpt-5-mini');
|
|
432
|
-
// or getModel('google', 'gemini-2.5-flash');
|
|
433
|
-
// or getModel('xai', 'grok-
|
|
434
|
-
// or getModel('groq', 'openai/gpt-oss-20b');
|
|
435
|
-
// or getModel('cerebras', 'gpt-oss-120b');
|
|
436
|
-
// or getModel('openrouter', 'z-ai/glm-4.5v');
|
|
794
|
+
const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
|
|
795
|
+
// or models.getModel('openai', 'gpt-5-mini');
|
|
796
|
+
// or models.getModel('google', 'gemini-2.5-flash');
|
|
797
|
+
// or models.getModel('xai', 'grok-4.5');
|
|
437
798
|
|
|
438
799
|
// Check if model supports reasoning
|
|
439
800
|
if (model.reasoning) {
|
|
@@ -441,10 +802,10 @@ if (model.reasoning) {
|
|
|
441
802
|
}
|
|
442
803
|
|
|
443
804
|
// Use the simplified reasoning option
|
|
444
|
-
const response = await completeSimple(model, {
|
|
445
|
-
messages: [{ role: 'user', content: 'Solve: 2x + 5 = 13' }]
|
|
805
|
+
const response = await models.completeSimple(model, {
|
|
806
|
+
messages: [{ role: 'user', content: 'Solve: 2x + 5 = 13', timestamp: Date.now() }]
|
|
446
807
|
}, {
|
|
447
|
-
reasoning: 'medium' // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
|
|
808
|
+
reasoning: 'medium' // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
448
809
|
});
|
|
449
810
|
|
|
450
811
|
// Access thinking and text blocks
|
|
@@ -457,35 +818,43 @@ for (const block of response.content) {
|
|
|
457
818
|
}
|
|
458
819
|
```
|
|
459
820
|
|
|
821
|
+
`xhigh` and `max` are model-specific, opt-in levels. Use `getSupportedThinkingLevels(model)` to determine whether a concrete model exposes either level; models such as GPT-5.6 can expose both.
|
|
822
|
+
|
|
460
823
|
### Provider-Specific Options (stream/complete)
|
|
461
824
|
|
|
462
|
-
|
|
825
|
+
`models.stream()`/`complete()` accept the owning API's full option set. Use `hasApi()` to narrow a dynamically looked-up model to its API for full option typing:
|
|
463
826
|
|
|
464
827
|
```typescript
|
|
465
|
-
import {
|
|
828
|
+
import { hasApi } from '@sammorrowdrums/mcpi-ai';
|
|
466
829
|
|
|
467
830
|
// OpenAI Reasoning (o1, o3, gpt-5)
|
|
468
|
-
const openaiModel = getModel('openai', 'gpt-5-mini')
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
831
|
+
const openaiModel = models.getModel('openai', 'gpt-5-mini')!;
|
|
832
|
+
if (hasApi(openaiModel, 'openai-responses')) {
|
|
833
|
+
await models.complete(openaiModel, context, {
|
|
834
|
+
reasoningEffort: 'medium',
|
|
835
|
+
reasoningSummary: 'detailed' // OpenAI Responses API only
|
|
836
|
+
});
|
|
837
|
+
}
|
|
473
838
|
|
|
474
|
-
// Anthropic Thinking
|
|
475
|
-
const anthropicModel = getModel('anthropic', 'claude-sonnet-4-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
839
|
+
// Anthropic Thinking
|
|
840
|
+
const anthropicModel = models.getModel('anthropic', 'claude-sonnet-4-5')!;
|
|
841
|
+
if (hasApi(anthropicModel, 'anthropic-messages')) {
|
|
842
|
+
await models.complete(anthropicModel, context, {
|
|
843
|
+
thinkingEnabled: true,
|
|
844
|
+
thinkingBudgetTokens: 8192 // Optional token limit
|
|
845
|
+
});
|
|
846
|
+
}
|
|
480
847
|
|
|
481
848
|
// Google Gemini Thinking
|
|
482
|
-
const googleModel = getModel('google', 'gemini-2.5-flash')
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
849
|
+
const googleModel = models.getModel('google', 'gemini-2.5-flash')!;
|
|
850
|
+
if (hasApi(googleModel, 'google-generative-ai')) {
|
|
851
|
+
await models.complete(googleModel, context, {
|
|
852
|
+
thinking: {
|
|
853
|
+
enabled: true,
|
|
854
|
+
budgetTokens: 8192 // -1 for dynamic, 0 to disable
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
489
858
|
```
|
|
490
859
|
|
|
491
860
|
### Streaming Thinking Content
|
|
@@ -493,7 +862,7 @@ await complete(googleModel, context, {
|
|
|
493
862
|
When streaming, thinking content is delivered through specific events:
|
|
494
863
|
|
|
495
864
|
```typescript
|
|
496
|
-
const s = streamSimple(model, context, { reasoning: 'high' });
|
|
865
|
+
const s = models.streamSimple(model, context, { reasoning: 'high' });
|
|
497
866
|
|
|
498
867
|
for await (const event of s) {
|
|
499
868
|
switch (event.type) {
|
|
@@ -514,7 +883,8 @@ for await (const event of s) {
|
|
|
514
883
|
|
|
515
884
|
Every `AssistantMessage` includes a `stopReason` field that indicates how the generation ended:
|
|
516
885
|
|
|
517
|
-
- `"
|
|
886
|
+
- `"pending"` - Only present in partial messages when we do not know what the stop reason will be
|
|
887
|
+
- `"stop"` - This is the final message the model will produce this turn
|
|
518
888
|
- `"length"` - Output hit the maximum token limit
|
|
519
889
|
- `"toolUse"` - Model is calling tools and expects tool results
|
|
520
890
|
- `"error"` - An error occurred during generation
|
|
@@ -524,11 +894,11 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
|
|
|
524
894
|
|
|
525
895
|
## Error Handling
|
|
526
896
|
|
|
527
|
-
|
|
897
|
+
Request failures never throw out of the stream functions: 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:
|
|
528
898
|
|
|
529
899
|
```typescript
|
|
530
900
|
// In streaming
|
|
531
|
-
for await (const event of
|
|
901
|
+
for await (const event of s) {
|
|
532
902
|
if (event.type === 'error') {
|
|
533
903
|
// event.reason is either "error" or "aborted"
|
|
534
904
|
// event.error is the AssistantMessage with partial content
|
|
@@ -538,7 +908,7 @@ for await (const event of stream) {
|
|
|
538
908
|
}
|
|
539
909
|
|
|
540
910
|
// The final message will have the error details
|
|
541
|
-
const message = await
|
|
911
|
+
const message = await s.result();
|
|
542
912
|
if (message.stopReason === 'error' || message.stopReason === 'aborted') {
|
|
543
913
|
console.error('Request failed:', message.errorMessage);
|
|
544
914
|
// message.content contains any partial content received before the error
|
|
@@ -546,21 +916,20 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
|
|
|
546
916
|
}
|
|
547
917
|
```
|
|
548
918
|
|
|
919
|
+
Auth failures (no key configured, OAuth refresh failed, unknown provider) surface the same way: as a stream error with `stopReason: "error"`.
|
|
920
|
+
|
|
549
921
|
### Aborting Requests
|
|
550
922
|
|
|
551
923
|
The abort signal allows you to cancel in-progress requests. Aborted requests have `stopReason === 'aborted'`:
|
|
552
924
|
|
|
553
925
|
```typescript
|
|
554
|
-
import { getModel, stream } from '@mariozechner/pi-ai';
|
|
555
|
-
|
|
556
|
-
const model = getModel('openai', 'gpt-4o-mini');
|
|
557
926
|
const controller = new AbortController();
|
|
558
927
|
|
|
559
928
|
// Abort after 2 seconds
|
|
560
929
|
setTimeout(() => controller.abort(), 2000);
|
|
561
930
|
|
|
562
|
-
const s = stream(model, {
|
|
563
|
-
messages: [{ role: 'user', content: 'Write a long story' }]
|
|
931
|
+
const s = models.stream(model, {
|
|
932
|
+
messages: [{ role: 'user', content: 'Write a long story', timestamp: Date.now() }]
|
|
564
933
|
}, {
|
|
565
934
|
signal: controller.signal
|
|
566
935
|
});
|
|
@@ -590,7 +959,7 @@ Aborted messages can be added to the conversation context and continued in subse
|
|
|
590
959
|
```typescript
|
|
591
960
|
const context = {
|
|
592
961
|
messages: [
|
|
593
|
-
{ role: 'user', content: 'Explain quantum computing in detail' }
|
|
962
|
+
{ role: 'user', content: 'Explain quantum computing in detail', timestamp: Date.now() }
|
|
594
963
|
]
|
|
595
964
|
};
|
|
596
965
|
|
|
@@ -598,14 +967,14 @@ const context = {
|
|
|
598
967
|
const controller1 = new AbortController();
|
|
599
968
|
setTimeout(() => controller1.abort(), 2000);
|
|
600
969
|
|
|
601
|
-
const partial = await complete(model, context, { signal: controller1.signal });
|
|
970
|
+
const partial = await models.complete(model, context, { signal: controller1.signal });
|
|
602
971
|
|
|
603
972
|
// Add the partial response to context
|
|
604
973
|
context.messages.push(partial);
|
|
605
|
-
context.messages.push({ role: 'user', content: 'Please continue' });
|
|
974
|
+
context.messages.push({ role: 'user', content: 'Please continue', timestamp: Date.now() });
|
|
606
975
|
|
|
607
976
|
// Continue the conversation
|
|
608
|
-
const continuation = await complete(model, context);
|
|
977
|
+
const continuation = await models.complete(model, context);
|
|
609
978
|
```
|
|
610
979
|
|
|
611
980
|
### Debugging Provider Payloads
|
|
@@ -613,7 +982,7 @@ const continuation = await complete(model, context);
|
|
|
613
982
|
Use the `onPayload` callback to inspect the request payload sent to the provider. This is useful for debugging request formatting issues or provider validation errors.
|
|
614
983
|
|
|
615
984
|
```typescript
|
|
616
|
-
const response = await complete(model, context, {
|
|
985
|
+
const response = await models.complete(model, context, {
|
|
617
986
|
onPayload: (payload) => {
|
|
618
987
|
console.log('Provider payload:', JSON.stringify(payload, null, 2));
|
|
619
988
|
}
|
|
@@ -622,205 +991,121 @@ const response = await complete(model, context, {
|
|
|
622
991
|
|
|
623
992
|
The callback is supported by `stream`, `complete`, `streamSimple`, and `completeSimple`.
|
|
624
993
|
|
|
625
|
-
##
|
|
626
|
-
|
|
627
|
-
The library uses a registry of API implementations. Built-in APIs include:
|
|
628
|
-
|
|
629
|
-
- **`anthropic-messages`**: Anthropic Messages API (`streamAnthropic`, `AnthropicOptions`)
|
|
630
|
-
- **`google-generative-ai`**: Google Generative AI API (`streamGoogle`, `GoogleOptions`)
|
|
631
|
-
- **`google-gemini-cli`**: Google Cloud Code Assist API (`streamGoogleGeminiCli`, `GoogleGeminiCliOptions`)
|
|
632
|
-
- **`google-vertex`**: Google Vertex AI API (`streamGoogleVertex`, `GoogleVertexOptions`)
|
|
633
|
-
- **`mistral-conversations`**: Mistral Conversations API (`streamMistral`, `MistralOptions`)
|
|
634
|
-
- **`openai-completions`**: OpenAI Chat Completions API (`streamOpenAICompletions`, `OpenAICompletionsOptions`)
|
|
635
|
-
- **`openai-responses`**: OpenAI Responses API (`streamOpenAIResponses`, `OpenAIResponsesOptions`)
|
|
636
|
-
- **`openai-codex-responses`**: OpenAI Codex Responses API (`streamOpenAICodexResponses`, `OpenAICodexResponsesOptions`)
|
|
637
|
-
- **`azure-openai-responses`**: Azure OpenAI Responses API (`streamAzureOpenAIResponses`, `AzureOpenAIResponsesOptions`)
|
|
638
|
-
- **`bedrock-converse-stream`**: Amazon Bedrock Converse API (`streamBedrock`, `BedrockOptions`)
|
|
994
|
+
## Custom Providers
|
|
639
995
|
|
|
640
|
-
###
|
|
996
|
+
### createProvider()
|
|
641
997
|
|
|
642
|
-
`
|
|
998
|
+
`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:
|
|
643
999
|
|
|
644
1000
|
```typescript
|
|
645
|
-
import {
|
|
646
|
-
|
|
647
|
-
fauxAssistantMessage,
|
|
648
|
-
fauxText,
|
|
649
|
-
fauxThinking,
|
|
650
|
-
fauxToolCall,
|
|
651
|
-
registerFauxProvider,
|
|
652
|
-
stream,
|
|
653
|
-
} from '@mariozechner/pi-ai';
|
|
654
|
-
|
|
655
|
-
const registration = registerFauxProvider({
|
|
656
|
-
tokensPerSecond: 50 // optional
|
|
657
|
-
});
|
|
1001
|
+
import { createModels, createProvider, envApiKeyAuth, type Model } from '@sammorrowdrums/mcpi-ai';
|
|
1002
|
+
import { openAICompletionsApi } from '@sammorrowdrums/mcpi-ai/api/openai-completions.lazy';
|
|
658
1003
|
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
1004
|
+
const ollamaModel: Model<'openai-completions'> = {
|
|
1005
|
+
id: 'llama-3.1-8b',
|
|
1006
|
+
name: 'Llama 3.1 8B (Ollama)',
|
|
1007
|
+
api: 'openai-completions',
|
|
1008
|
+
provider: 'ollama',
|
|
1009
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
1010
|
+
reasoning: false,
|
|
1011
|
+
input: ['text'],
|
|
1012
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1013
|
+
contextWindow: 128000,
|
|
1014
|
+
maxTokens: 32000
|
|
662
1015
|
};
|
|
663
1016
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
sessionId: 'session-1',
|
|
673
|
-
cacheRetention: 'short'
|
|
1017
|
+
const ollama = createProvider({
|
|
1018
|
+
id: 'ollama',
|
|
1019
|
+
name: 'Ollama',
|
|
1020
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
1021
|
+
// Every provider declares auth; keyless local servers resolve as configured with no key.
|
|
1022
|
+
auth: { apiKey: { name: 'Ollama', resolve: async () => ({ auth: {} }) } },
|
|
1023
|
+
models: [ollamaModel],
|
|
1024
|
+
api: openAICompletionsApi(),
|
|
674
1025
|
});
|
|
675
|
-
context.messages.push(first);
|
|
676
1026
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
toolCallId: first.content.find((block) => block.type === 'toolCall')!.id,
|
|
680
|
-
toolName: 'echo',
|
|
681
|
-
content: [{ type: 'text', text: 'package.json contents here' }],
|
|
682
|
-
isError: false,
|
|
683
|
-
timestamp: Date.now()
|
|
684
|
-
});
|
|
1027
|
+
const models = createModels();
|
|
1028
|
+
models.setProvider(ollama);
|
|
685
1029
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
fauxThinking('Now I can summarize the tool output.'),
|
|
689
|
-
fauxText('Here is the summary.')
|
|
690
|
-
])
|
|
691
|
-
]);
|
|
1030
|
+
await models.complete(models.getModel('ollama', 'llama-3.1-8b')!, context);
|
|
1031
|
+
```
|
|
692
1032
|
|
|
693
|
-
|
|
694
|
-
for await (const event of s) {
|
|
695
|
-
console.log(event.type);
|
|
696
|
-
}
|
|
1033
|
+
For providers with real keys, `envApiKeyAuth(displayName, envVars)` gives the standard behavior (stored credential wins, then the first set env var):
|
|
697
1034
|
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1035
|
+
```typescript
|
|
1036
|
+
const proxy = createProvider({
|
|
1037
|
+
id: 'my-proxy',
|
|
1038
|
+
auth: { apiKey: envApiKeyAuth('My proxy API key', ['MY_PROXY_API_KEY']) },
|
|
1039
|
+
models: [/* ... */],
|
|
1040
|
+
api: openAICompletionsApi(),
|
|
704
1041
|
});
|
|
705
|
-
const thinker = multiModel.getModel('faux-thinker');
|
|
706
|
-
|
|
707
|
-
console.log(thinker?.reasoning);
|
|
708
|
-
console.log(registration.getPendingResponseCount());
|
|
709
|
-
console.log(registration.state.callCount);
|
|
710
|
-
registration.unregister();
|
|
711
|
-
multiModel.unregister();
|
|
712
1042
|
```
|
|
713
1043
|
|
|
714
|
-
|
|
715
|
-
- Responses are consumed from a queue in request start order.
|
|
716
|
-
- If the queue is empty, the faux provider returns an assistant error message with `errorMessage: "No more faux responses queued"`.
|
|
717
|
-
- Use `registration.setResponses([...])` to replace the remaining queue and `registration.appendResponses([...])` to add more responses.
|
|
718
|
-
- `registration.models` exposes all registered faux models. `registration.getModel()` returns the first one, and `registration.getModel(id)` returns a specific one.
|
|
719
|
-
- Use `fauxAssistantMessage(...)` for scripted assistant replies. Use `fauxText(...)`, `fauxThinking(...)`, and `fauxToolCall(...)` to build content blocks without filling in low-level fields manually.
|
|
720
|
-
- `registration.unregister()` removes the temporary provider from the global API registry.
|
|
721
|
-
- Usage is estimated at roughly 1 token per 4 characters. When `sessionId` is present and `cacheRetention` is not `"none"`, prompt cache reads and writes are simulated automatically.
|
|
722
|
-
- Tool call arguments stream incrementally via `toolcall_delta` chunks.
|
|
723
|
-
- By default, each streamed chunk is emitted on its own microtask. Set `tokensPerSecond` to pace chunk delivery in real time.
|
|
724
|
-
- The intended use is one deterministic scripted flow per registration. If you need independent concurrent flows, register separate faux providers.
|
|
725
|
-
|
|
726
|
-
### Providers and Models
|
|
1044
|
+
Mixed-API providers pass a map keyed by `model.api`; each model dispatches to its API's implementation:
|
|
727
1045
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
-
|
|
1046
|
+
```typescript
|
|
1047
|
+
import { anthropicMessagesApi } from '@sammorrowdrums/mcpi-ai/api/anthropic-messages.lazy';
|
|
1048
|
+
import { openAIResponsesApi } from '@sammorrowdrums/mcpi-ai/api/openai-responses.lazy';
|
|
1049
|
+
|
|
1050
|
+
const gateway = createProvider({
|
|
1051
|
+
id: 'my-gateway',
|
|
1052
|
+
auth: { apiKey: envApiKeyAuth('Gateway key', ['GATEWAY_API_KEY']) },
|
|
1053
|
+
models: [/* models with api: 'anthropic-messages' or 'openai-responses' */],
|
|
1054
|
+
api: {
|
|
1055
|
+
'anthropic-messages': anthropicMessagesApi(),
|
|
1056
|
+
'openai-responses': openAIResponsesApi(),
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
```
|
|
734
1060
|
|
|
735
|
-
|
|
1061
|
+
Provider-wide endpoint or request transformations belong in the provider's API implementation: wrap the `ProviderStreams` you pass as `api` so every request goes through the transformation before dispatch. The Cloudflare providers do this to materialize account/gateway endpoint placeholders from the resolved provider env:
|
|
736
1062
|
|
|
737
1063
|
```typescript
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
// Get all models from a provider (fully typed)
|
|
745
|
-
const anthropicModels = getModels('anthropic');
|
|
746
|
-
for (const model of anthropicModels) {
|
|
747
|
-
console.log(`${model.id}: ${model.name}`);
|
|
748
|
-
console.log(` API: ${model.api}`); // 'anthropic-messages'
|
|
749
|
-
console.log(` Context: ${model.contextWindow} tokens`);
|
|
750
|
-
console.log(` Vision: ${model.input.includes('image')}`);
|
|
751
|
-
console.log(` Reasoning: ${model.reasoning}`);
|
|
1064
|
+
function tenantStreams(streams: ProviderStreams): ProviderStreams {
|
|
1065
|
+
const withTenant = (model: Model<Api>) => ({ ...model, baseUrl: model.baseUrl.replace('{tenant}', tenantId) });
|
|
1066
|
+
return {
|
|
1067
|
+
stream: (model, context, options) => streams.stream(withTenant(model), context, options),
|
|
1068
|
+
streamSimple: (model, context, options) => streams.streamSimple(withTenant(model), context, options),
|
|
1069
|
+
};
|
|
752
1070
|
}
|
|
753
1071
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1072
|
+
const tenantGateway = createProvider({
|
|
1073
|
+
id: 'tenant-gateway',
|
|
1074
|
+
auth: { apiKey: envApiKeyAuth('Gateway key', ['GATEWAY_API_KEY']) },
|
|
1075
|
+
models: [/* ... */],
|
|
1076
|
+
api: tenantStreams(openAICompletionsApi()),
|
|
1077
|
+
});
|
|
757
1078
|
```
|
|
758
1079
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
You can create custom models for local inference servers or custom endpoints:
|
|
1080
|
+
Dynamic model lists use `fetchModels`. `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.
|
|
762
1081
|
|
|
763
1082
|
```typescript
|
|
764
|
-
|
|
1083
|
+
const models = createModels({ credentials, modelsStore });
|
|
1084
|
+
const llamacpp = createProvider({
|
|
1085
|
+
id: 'llamacpp',
|
|
1086
|
+
auth: { apiKey: { name: 'llama.cpp', resolve: async () => ({ auth: {} }) } },
|
|
1087
|
+
models: [],
|
|
1088
|
+
fetchModels: async ({ signal }) => fetchModelsFromServer('http://localhost:8080', signal),
|
|
1089
|
+
api: openAICompletionsApi(),
|
|
1090
|
+
});
|
|
765
1091
|
|
|
766
|
-
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
provider: 'ollama',
|
|
772
|
-
baseUrl: 'http://localhost:11434/v1',
|
|
773
|
-
reasoning: false,
|
|
774
|
-
input: ['text'],
|
|
775
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
776
|
-
contextWindow: 128000,
|
|
777
|
-
maxTokens: 32000
|
|
778
|
-
};
|
|
1092
|
+
models.setProvider(llamacpp);
|
|
1093
|
+
const result = await models.refresh({ signal });
|
|
1094
|
+
if (result.aborted) console.log('refresh cancelled');
|
|
1095
|
+
for (const [provider, error] of result.errors) console.error(provider, error);
|
|
1096
|
+
```
|
|
779
1097
|
|
|
780
|
-
|
|
781
|
-
const litellmModel: Model<'openai-completions'> = {
|
|
782
|
-
id: 'gpt-4o',
|
|
783
|
-
name: 'GPT-4o (via LiteLLM)',
|
|
784
|
-
api: 'openai-completions',
|
|
785
|
-
provider: 'litellm',
|
|
786
|
-
baseUrl: 'http://localhost:4000/v1',
|
|
787
|
-
reasoning: false,
|
|
788
|
-
input: ['text', 'image'],
|
|
789
|
-
cost: { input: 2.5, output: 10, cacheRead: 0, cacheWrite: 0 },
|
|
790
|
-
contextWindow: 128000,
|
|
791
|
-
maxTokens: 16384,
|
|
792
|
-
compat: {
|
|
793
|
-
supportsStore: false, // LiteLLM doesn't support the store field
|
|
794
|
-
}
|
|
795
|
-
};
|
|
1098
|
+
`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.
|
|
796
1099
|
|
|
797
|
-
|
|
798
|
-
const proxyModel: Model<'anthropic-messages'> = {
|
|
799
|
-
id: 'claude-sonnet-4',
|
|
800
|
-
name: 'Claude Sonnet 4 (Proxied)',
|
|
801
|
-
api: 'anthropic-messages',
|
|
802
|
-
provider: 'custom-proxy',
|
|
803
|
-
baseUrl: 'https://proxy.example.com/v1',
|
|
804
|
-
reasoning: true,
|
|
805
|
-
input: ['text', 'image'],
|
|
806
|
-
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
807
|
-
contextWindow: 200000,
|
|
808
|
-
maxTokens: 8192,
|
|
809
|
-
headers: {
|
|
810
|
-
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
|
811
|
-
'X-Custom-Auth': 'bearer-token-here'
|
|
812
|
-
}
|
|
813
|
-
};
|
|
1100
|
+
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.
|
|
814
1101
|
|
|
815
|
-
|
|
816
|
-
const response = await stream(ollamaModel, context, {
|
|
817
|
-
apiKey: 'dummy' // Ollama doesn't need a real key
|
|
818
|
-
});
|
|
819
|
-
```
|
|
1102
|
+
`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. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
|
|
820
1103
|
|
|
821
|
-
|
|
1104
|
+
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).
|
|
822
1105
|
|
|
823
|
-
|
|
1106
|
+
Some OpenAI-compatible servers do not understand the `developer` role used for reasoning-capable models. For those providers, set `compat.supportsDeveloperRole` to `false` so the system prompt is sent as a `system` message instead. If the server also does not support `reasoning_effort`, set `compat.supportsReasoningEffort` to `false` too. This commonly applies to Ollama, vLLM, SGLang, and similar OpenAI-compatible servers.
|
|
1107
|
+
|
|
1108
|
+
Use model-level `thinkingLevelMap` to describe model-specific thinking controls. Keys are mcpi thinking levels (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`). Missing standard levels through `high` use provider defaults; `xhigh` and `max` are opt-in and require a non-null map entry. String values are sent to the provider, `null` marks a level unsupported, and maps may skip levels.
|
|
824
1109
|
|
|
825
1110
|
```typescript
|
|
826
1111
|
const ollamaReasoningModel: Model<'openai-completions'> = {
|
|
@@ -834,6 +1119,13 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
|
|
|
834
1119
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
835
1120
|
contextWindow: 131072,
|
|
836
1121
|
maxTokens: 32000,
|
|
1122
|
+
thinkingLevelMap: {
|
|
1123
|
+
minimal: null,
|
|
1124
|
+
low: null,
|
|
1125
|
+
medium: null,
|
|
1126
|
+
high: 'high',
|
|
1127
|
+
xhigh: null,
|
|
1128
|
+
},
|
|
837
1129
|
compat: {
|
|
838
1130
|
supportsDeveloperRole: false,
|
|
839
1131
|
supportsReasoningEffort: false,
|
|
@@ -841,9 +1133,39 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
|
|
|
841
1133
|
};
|
|
842
1134
|
```
|
|
843
1135
|
|
|
1136
|
+
### Calling API Implementations Directly
|
|
1137
|
+
|
|
1138
|
+
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:
|
|
1139
|
+
|
|
1140
|
+
```typescript
|
|
1141
|
+
import { stream } from '@sammorrowdrums/mcpi-ai/api/anthropic-messages';
|
|
1142
|
+
|
|
1143
|
+
const s = stream(claudeModel, context, {
|
|
1144
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
1145
|
+
thinkingEnabled: true,
|
|
1146
|
+
thinkingBudgetTokens: 2048,
|
|
1147
|
+
});
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
Built-in API implementations live under `./api/<api-id>`:
|
|
1151
|
+
|
|
1152
|
+
| API id | Options type |
|
|
1153
|
+
|--------|--------------|
|
|
1154
|
+
| `anthropic-messages` | `AnthropicOptions` |
|
|
1155
|
+
| `openai-completions` | `OpenAICompletionsOptions` |
|
|
1156
|
+
| `openai-responses` | `OpenAIResponsesOptions` |
|
|
1157
|
+
| `openai-codex-responses` | `OpenAICodexResponsesOptions` |
|
|
1158
|
+
| `azure-openai-responses` | `AzureOpenAIResponsesOptions` |
|
|
1159
|
+
| `google-generative-ai` | `GoogleOptions` |
|
|
1160
|
+
| `google-vertex` | `GoogleVertexOptions` |
|
|
1161
|
+
| `mistral-conversations` | `MistralOptions` |
|
|
1162
|
+
| `bedrock-converse-stream` | `BedrockOptions` |
|
|
1163
|
+
|
|
1164
|
+
Importing an implementation module loads its SDK. The `./api/<id>.lazy` wrappers (used by the provider factories) defer that load to the first request when the runtime or bundler supports dynamic import chunking. Legacy raw API subpaths from older releases (`./anthropic`, `./google`, `./mistral`, `./openai-completions`, ...) were removed; use `@sammorrowdrums/mcpi-ai/api/<api-id>`.
|
|
1165
|
+
|
|
844
1166
|
### OpenAI Compatibility Settings
|
|
845
1167
|
|
|
846
|
-
The `openai-completions` API is implemented by many providers with minor differences. By default, the library auto-detects compatibility settings based on `baseUrl` for a small set of known OpenAI-compatible providers (Cerebras, xAI, Chutes, DeepSeek, zAi, OpenCode, etc.). For custom proxies or unknown endpoints, you can override these settings via the `compat` field. For `openai-responses` models, the compat field
|
|
1168
|
+
The `openai-completions` API is implemented by many providers with minor differences. By default, the library auto-detects compatibility settings based on `baseUrl` for a small set of known OpenAI-compatible providers (Cerebras, xAI, Chutes, DeepSeek, NVIDIA NIM, Together AI, zAi, OpenCode, Cloudflare Workers AI, etc.). For custom proxies or unknown endpoints, you can override these settings via the `compat` field. For `openai-responses` models, the compat field supports Responses-specific flags.
|
|
847
1169
|
|
|
848
1170
|
```typescript
|
|
849
1171
|
interface OpenAICompletionsCompat {
|
|
@@ -852,19 +1174,28 @@ interface OpenAICompletionsCompat {
|
|
|
852
1174
|
supportsReasoningEffort?: boolean; // Whether provider supports `reasoning_effort` (default: true)
|
|
853
1175
|
supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
|
|
854
1176
|
supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: true)
|
|
855
|
-
|
|
1177
|
+
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)
|
|
1178
|
+
sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: false)
|
|
1179
|
+
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)
|
|
856
1180
|
maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
|
|
857
1181
|
requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
|
|
858
1182
|
requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
|
|
859
1183
|
requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false)
|
|
860
|
-
|
|
1184
|
+
requiresReasoningContentOnAssistantMessages?: boolean; // Whether all replayed assistant messages must include empty reasoning_content when reasoning is enabled (default: auto-detected for DeepSeek)
|
|
1185
|
+
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)
|
|
1186
|
+
chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for mcpi-controlled thinking values
|
|
1187
|
+
chatTemplateArgs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_args values for thinkingFormat: 'baseten'; use $var for mcpi-controlled thinking values
|
|
861
1188
|
cacheControlFormat?: 'anthropic'; // Anthropic-style cache_control on system prompt, last tool, and last user/assistant text content
|
|
862
1189
|
openRouterRouting?: OpenRouterRouting; // OpenRouter routing preferences (default: {})
|
|
863
1190
|
vercelGatewayRouting?: VercelGatewayRouting; // Vercel AI Gateway routing preferences (default: {})
|
|
864
1191
|
}
|
|
865
1192
|
|
|
866
1193
|
interface OpenAIResponsesCompat {
|
|
867
|
-
//
|
|
1194
|
+
supportsDeveloperRole?: boolean; // Whether provider supports `developer` role vs `system` (default: true)
|
|
1195
|
+
sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Session-affinity header format: 'openai' sends `session_id` and `x-client-request-id`; 'openai-nosession' sends `x-client-request-id`; 'openrouter' sends `x-session-id`. Does not affect the `prompt_cache_key` body param (default: auto-detected)
|
|
1196
|
+
supportsLongCacheRetention?: boolean; // Whether provider supports `prompt_cache_retention: "24h"` (default: true)
|
|
1197
|
+
supportsStrictMode?: boolean; // Whether provider supports strict JSON-schema function tools (default: false; enabled in metadata for built-in OpenAI models)
|
|
1198
|
+
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)
|
|
868
1199
|
}
|
|
869
1200
|
```
|
|
870
1201
|
|
|
@@ -874,30 +1205,97 @@ If `compat` is not set, the library falls back to URL-based detection. If `compa
|
|
|
874
1205
|
- **Custom inference servers**: May use non-standard field names
|
|
875
1206
|
- **Self-hosted endpoints**: May have different feature support
|
|
876
1207
|
|
|
877
|
-
|
|
1208
|
+
## Faux Provider for Tests
|
|
878
1209
|
|
|
879
|
-
|
|
1210
|
+
`fauxProvider()` builds an in-memory provider with scripted responses for tests and demos:
|
|
880
1211
|
|
|
881
1212
|
```typescript
|
|
882
|
-
import {
|
|
1213
|
+
import {
|
|
1214
|
+
createModels,
|
|
1215
|
+
fauxAssistantMessage,
|
|
1216
|
+
fauxProvider,
|
|
1217
|
+
fauxText,
|
|
1218
|
+
fauxThinking,
|
|
1219
|
+
fauxToolCall,
|
|
1220
|
+
} from '@sammorrowdrums/mcpi-ai';
|
|
883
1221
|
|
|
884
|
-
|
|
885
|
-
|
|
1222
|
+
const faux = fauxProvider({
|
|
1223
|
+
tokensPerSecond: 50 // optional
|
|
1224
|
+
});
|
|
886
1225
|
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
1226
|
+
const models = createModels();
|
|
1227
|
+
models.setProvider(faux.provider);
|
|
1228
|
+
|
|
1229
|
+
const model = faux.getModel();
|
|
1230
|
+
const context = {
|
|
1231
|
+
messages: [{ role: 'user', content: 'Summarize package.json and then call echo', timestamp: Date.now() }]
|
|
890
1232
|
};
|
|
891
1233
|
|
|
892
|
-
|
|
1234
|
+
faux.setResponses([
|
|
1235
|
+
fauxAssistantMessage([
|
|
1236
|
+
fauxThinking('Need to inspect package metadata first.'),
|
|
1237
|
+
fauxToolCall('echo', { text: 'package.json' })
|
|
1238
|
+
], { stopReason: 'toolUse' })
|
|
1239
|
+
]);
|
|
1240
|
+
|
|
1241
|
+
const first = await models.complete(model, context, {
|
|
1242
|
+
sessionId: 'session-1',
|
|
1243
|
+
cacheRetention: 'short'
|
|
1244
|
+
});
|
|
1245
|
+
context.messages.push(first);
|
|
1246
|
+
|
|
1247
|
+
context.messages.push({
|
|
1248
|
+
role: 'toolResult',
|
|
1249
|
+
toolCallId: first.content.find((block) => block.type === 'toolCall')!.id,
|
|
1250
|
+
toolName: 'echo',
|
|
1251
|
+
content: [{ type: 'text', text: 'package.json contents here' }],
|
|
1252
|
+
isError: false,
|
|
1253
|
+
timestamp: Date.now()
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
faux.setResponses([
|
|
1257
|
+
fauxAssistantMessage([
|
|
1258
|
+
fauxThinking('Now I can summarize the tool output.'),
|
|
1259
|
+
fauxText('Here is the summary.')
|
|
1260
|
+
])
|
|
1261
|
+
]);
|
|
1262
|
+
|
|
1263
|
+
const s = models.stream(model, context);
|
|
1264
|
+
for await (const event of s) {
|
|
1265
|
+
console.log(event.type);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// Optional: multiple faux models for model-switching tests
|
|
1269
|
+
const multiModel = fauxProvider({
|
|
1270
|
+
provider: 'faux-multi',
|
|
1271
|
+
models: [
|
|
1272
|
+
{ id: 'faux-fast', reasoning: false },
|
|
1273
|
+
{ id: 'faux-thinker', reasoning: true }
|
|
1274
|
+
]
|
|
1275
|
+
});
|
|
1276
|
+
models.setProvider(multiModel.provider);
|
|
1277
|
+
const thinker = multiModel.getModel('faux-thinker');
|
|
1278
|
+
|
|
1279
|
+
console.log(thinker?.reasoning);
|
|
1280
|
+
console.log(faux.getPendingResponseCount());
|
|
1281
|
+
console.log(faux.state.callCount);
|
|
893
1282
|
```
|
|
894
1283
|
|
|
1284
|
+
Notes:
|
|
1285
|
+
- Responses are consumed from a queue in request start order.
|
|
1286
|
+
- If the queue is empty, the faux provider returns an assistant error message with `errorMessage: "No more faux responses queued"`.
|
|
1287
|
+
- Use `faux.setResponses([...])` to replace the remaining queue and `faux.appendResponses([...])` to add more responses.
|
|
1288
|
+
- `faux.models` exposes all faux models. `faux.getModel()` returns the first one, and `faux.getModel(id)` returns a specific one.
|
|
1289
|
+
- Use `fauxAssistantMessage(...)` for scripted assistant replies. Use `fauxText(...)`, `fauxThinking(...)`, and `fauxToolCall(...)` to build content blocks without filling in low-level fields manually.
|
|
1290
|
+
- Usage is estimated at roughly 1 token per 4 characters. When `sessionId` is present and `cacheRetention` is not `"none"`, prompt cache reads and writes are simulated automatically.
|
|
1291
|
+
- Tool call arguments stream incrementally via `toolcall_delta` chunks.
|
|
1292
|
+
- By default, each streamed chunk is emitted on its own microtask. Set `tokensPerSecond` to pace chunk delivery in real time.
|
|
1293
|
+
- The intended use is one deterministic scripted flow per handle. If you need independent concurrent flows, create separate faux providers with distinct `provider` ids.
|
|
1294
|
+
|
|
895
1295
|
## Cross-Provider Handoffs
|
|
896
1296
|
|
|
897
1297
|
The library supports seamless handoffs between different LLM providers within the same conversation. This allows you to switch models mid-conversation while preserving context, including thinking blocks, tool calls, and tool results.
|
|
898
1298
|
|
|
899
|
-
### How It Works
|
|
900
|
-
|
|
901
1299
|
When messages from one provider are sent to a different provider, the library automatically transforms them for compatibility:
|
|
902
1300
|
|
|
903
1301
|
- **User and tool result messages** are passed through unchanged
|
|
@@ -905,98 +1303,86 @@ When messages from one provider are sent to a different provider, the library au
|
|
|
905
1303
|
- **Assistant messages from different providers** have their thinking blocks converted to text with `<thinking>` tags
|
|
906
1304
|
- **Tool calls and regular text** are preserved unchanged
|
|
907
1305
|
|
|
908
|
-
### Example: Multi-Provider Conversation
|
|
909
|
-
|
|
910
1306
|
```typescript
|
|
911
|
-
import {
|
|
1307
|
+
import { createModels, type Context } from '@sammorrowdrums/mcpi-ai';
|
|
1308
|
+
import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
|
|
1309
|
+
import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
|
|
1310
|
+
import { googleProvider } from '@sammorrowdrums/mcpi-ai/providers/google';
|
|
912
1311
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
};
|
|
1312
|
+
const models = createModels();
|
|
1313
|
+
models.setProvider(anthropicProvider());
|
|
1314
|
+
models.setProvider(openaiProvider());
|
|
1315
|
+
models.setProvider(googleProvider());
|
|
918
1316
|
|
|
919
|
-
context
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
context.messages.push(
|
|
1317
|
+
const context: Context = { messages: [] };
|
|
1318
|
+
|
|
1319
|
+
// Start with Claude
|
|
1320
|
+
const claude = models.getModel('anthropic', 'claude-sonnet-4-5')!;
|
|
1321
|
+
context.messages.push({ role: 'user', content: 'What is 25 * 18?', timestamp: Date.now() });
|
|
1322
|
+
context.messages.push(await models.completeSimple(claude, context, { reasoning: 'medium' }));
|
|
924
1323
|
|
|
925
1324
|
// Switch to GPT-5 - it will see Claude's thinking as <thinking> tagged text
|
|
926
|
-
const gpt5 = getModel('openai', 'gpt-5-mini')
|
|
927
|
-
context.messages.push({ role: 'user', content: 'Is that calculation correct?' });
|
|
928
|
-
|
|
929
|
-
context.messages.push(gptResponse);
|
|
1325
|
+
const gpt5 = models.getModel('openai', 'gpt-5-mini')!;
|
|
1326
|
+
context.messages.push({ role: 'user', content: 'Is that calculation correct?', timestamp: Date.now() });
|
|
1327
|
+
context.messages.push(await models.complete(gpt5, context));
|
|
930
1328
|
|
|
931
1329
|
// Switch to Gemini
|
|
932
|
-
const gemini = getModel('google', 'gemini-2.5-flash')
|
|
933
|
-
context.messages.push({ role: 'user', content: 'What was the original question?' });
|
|
934
|
-
const geminiResponse = await complete(gemini, context);
|
|
1330
|
+
const gemini = models.getModel('google', 'gemini-2.5-flash')!;
|
|
1331
|
+
context.messages.push({ role: 'user', content: 'What was the original question?', timestamp: Date.now() });
|
|
1332
|
+
const geminiResponse = await models.complete(gemini, context);
|
|
935
1333
|
```
|
|
936
1334
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
All providers can handle messages from other providers, including:
|
|
940
|
-
- Text content
|
|
941
|
-
- Tool calls and tool results (including images in tool results)
|
|
942
|
-
- Thinking/reasoning blocks (transformed to tagged text for cross-provider compatibility)
|
|
943
|
-
- Aborted messages with partial content
|
|
944
|
-
|
|
945
|
-
This enables flexible workflows where you can:
|
|
946
|
-
- Start with a fast model for initial responses
|
|
947
|
-
- Switch to a more capable model for complex reasoning
|
|
948
|
-
- Use specialized models for specific tasks
|
|
949
|
-
- Maintain conversation continuity across provider outages
|
|
1335
|
+
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.
|
|
950
1336
|
|
|
951
1337
|
## Context Serialization
|
|
952
1338
|
|
|
953
1339
|
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:
|
|
954
1340
|
|
|
955
1341
|
```typescript
|
|
956
|
-
import { Context, getModel, complete } from '@mariozechner/pi-ai';
|
|
957
|
-
|
|
958
|
-
// Create and use a context
|
|
959
1342
|
const context: Context = {
|
|
960
1343
|
systemPrompt: 'You are a helpful assistant.',
|
|
961
1344
|
messages: [
|
|
962
|
-
{ role: 'user', content: 'What is TypeScript?' }
|
|
1345
|
+
{ role: 'user', content: 'What is TypeScript?', timestamp: Date.now() }
|
|
963
1346
|
]
|
|
964
1347
|
};
|
|
965
1348
|
|
|
966
|
-
const model = getModel('openai', 'gpt-4o-mini')
|
|
967
|
-
const response = await complete(model, context);
|
|
1349
|
+
const model = models.getModel('openai', 'gpt-4o-mini')!;
|
|
1350
|
+
const response = await models.complete(model, context);
|
|
968
1351
|
context.messages.push(response);
|
|
969
1352
|
|
|
970
1353
|
// Serialize the entire context
|
|
971
1354
|
const serialized = JSON.stringify(context);
|
|
972
|
-
console.log('Serialized context size:', serialized.length, 'bytes');
|
|
973
1355
|
|
|
974
1356
|
// Save to database, localStorage, file, etc.
|
|
975
1357
|
localStorage.setItem('conversation', serialized);
|
|
976
1358
|
|
|
977
1359
|
// Later: deserialize and continue the conversation
|
|
978
1360
|
const restored: Context = JSON.parse(localStorage.getItem('conversation')!);
|
|
979
|
-
restored.messages.push({ role: 'user', content: 'Tell me more about its type system' });
|
|
1361
|
+
restored.messages.push({ role: 'user', content: 'Tell me more about its type system', timestamp: Date.now() });
|
|
980
1362
|
|
|
981
1363
|
// Continue with any model
|
|
982
|
-
const newModel = getModel('anthropic', 'claude-3-5-haiku-20241022')
|
|
983
|
-
const continuation = await complete(newModel, restored);
|
|
1364
|
+
const newModel = models.getModel('anthropic', 'claude-3-5-haiku-20241022')!;
|
|
1365
|
+
const continuation = await models.complete(newModel, restored);
|
|
984
1366
|
```
|
|
985
1367
|
|
|
1368
|
+
Models are plain serializable data too — no functions or implementations attached — so persisting "which model was this conversation using" is a `JSON.stringify` away.
|
|
1369
|
+
|
|
986
1370
|
> **Note**: If the context contains images (encoded as base64 as shown in the Image Input section), those will also be serialized.
|
|
987
1371
|
|
|
988
1372
|
## Browser Usage
|
|
989
1373
|
|
|
990
|
-
The library supports browser environments.
|
|
1374
|
+
The library supports browser environments. The core entrypoint and provider factories are side-effect free and bundle cleanly. Environment variables are not available in browsers, so pass API keys explicitly — or inject a `CredentialStore` (e.g. localStorage-backed) and let provider auth resolve from stored credentials:
|
|
991
1375
|
|
|
992
1376
|
```typescript
|
|
993
|
-
import {
|
|
1377
|
+
import { createModels } from '@sammorrowdrums/mcpi-ai';
|
|
1378
|
+
import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
|
|
994
1379
|
|
|
995
|
-
|
|
996
|
-
|
|
1380
|
+
const models = createModels();
|
|
1381
|
+
models.setProvider(anthropicProvider());
|
|
997
1382
|
|
|
998
|
-
const
|
|
999
|
-
|
|
1383
|
+
const model = models.getModel('anthropic', 'claude-3-5-haiku-20241022')!;
|
|
1384
|
+
const response = await models.complete(model, {
|
|
1385
|
+
messages: [{ role: 'user', content: 'Hello!', timestamp: Date.now() }]
|
|
1000
1386
|
}, {
|
|
1001
1387
|
apiKey: 'your-api-key'
|
|
1002
1388
|
});
|
|
@@ -1004,95 +1390,133 @@ const response = await complete(model, {
|
|
|
1004
1390
|
|
|
1005
1391
|
> **Security Warning**: Exposing API keys in frontend code is dangerous. Anyone can extract and abuse your keys. Only use this approach for internal tools or demos. For production applications, use a backend proxy that keeps your API keys secure.
|
|
1006
1392
|
|
|
1007
|
-
|
|
1393
|
+
Browser compatibility notes:
|
|
1008
1394
|
|
|
1009
|
-
- Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments.
|
|
1010
|
-
- OAuth login flows are not
|
|
1011
|
-
- In browser builds, Bedrock can still appear in model lists. Calls to Bedrock models fail at runtime.
|
|
1395
|
+
- Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments. It can still appear in model lists; calls fail at runtime.
|
|
1396
|
+
- OAuth login flows are Node-only. They are lazy-loaded behind bundler-opaque imports, so registering an OAuth-capable provider does not pull Node-only code into a browser bundle — only actually logging in would.
|
|
1012
1397
|
- Use a server-side proxy or backend service if you need Bedrock or OAuth-based auth from a web app.
|
|
1013
1398
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
In Node.js environments, you can set environment variables to avoid passing API keys:
|
|
1017
|
-
|
|
1018
|
-
| Provider | Environment Variable(s) |
|
|
1019
|
-
|----------|------------------------|
|
|
1020
|
-
| OpenAI | `OPENAI_API_KEY` |
|
|
1021
|
-
| Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME` (optional `AZURE_OPENAI_API_VERSION`, `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` like `model=deployment,model2=deployment2`) |
|
|
1022
|
-
| Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
|
|
1023
|
-
| Google | `GEMINI_API_KEY` |
|
|
1024
|
-
| Vertex AI | `GOOGLE_CLOUD_API_KEY` or `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) + `GOOGLE_CLOUD_LOCATION` + ADC |
|
|
1025
|
-
| Mistral | `MISTRAL_API_KEY` |
|
|
1026
|
-
| Groq | `GROQ_API_KEY` |
|
|
1027
|
-
| Cerebras | `CEREBRAS_API_KEY` |
|
|
1028
|
-
| xAI | `XAI_API_KEY` |
|
|
1029
|
-
| Fireworks | `FIREWORKS_API_KEY` |
|
|
1030
|
-
| OpenRouter | `OPENROUTER_API_KEY` |
|
|
1031
|
-
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
|
|
1032
|
-
| zAI | `ZAI_API_KEY` |
|
|
1033
|
-
| MiniMax | `MINIMAX_API_KEY` |
|
|
1034
|
-
| OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
|
|
1035
|
-
| Kimi For Coding | `KIMI_API_KEY` |
|
|
1036
|
-
| GitHub Copilot | `COPILOT_GITHUB_TOKEN` or `GH_TOKEN` or `GITHUB_TOKEN` |
|
|
1399
|
+
## Bundling and Tree Shaking
|
|
1037
1400
|
|
|
1038
|
-
|
|
1401
|
+
For small bundles, import only the providers you need:
|
|
1039
1402
|
|
|
1040
1403
|
```typescript
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
const response = await complete(model, context);
|
|
1404
|
+
import { createModels } from '@sammorrowdrums/mcpi-ai';
|
|
1405
|
+
import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
|
|
1044
1406
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
apiKey: 'sk-different-key'
|
|
1048
|
-
});
|
|
1407
|
+
const models = createModels();
|
|
1408
|
+
models.setProvider(openaiProvider());
|
|
1049
1409
|
```
|
|
1050
1410
|
|
|
1051
|
-
|
|
1411
|
+
Rules:
|
|
1052
1412
|
|
|
1053
|
-
|
|
1413
|
+
- `@sammorrowdrums/mcpi-ai` is the core entrypoint and does not import built-in catalogs, provider factories, or SDK implementations.
|
|
1414
|
+
- `@sammorrowdrums/mcpi-ai/providers/<provider>` imports that provider's catalog and lazy API wrapper only.
|
|
1415
|
+
- `@sammorrowdrums/mcpi-ai/providers/all` imports every built-in provider factory and all catalogs. Use it only when you want the full built-in set.
|
|
1416
|
+
- With code splitting, provider SDKs stay in lazy chunks and load on first request.
|
|
1417
|
+
- Without code splitting, bundlers fold reachable lazy API implementations into the single bundle. A single-provider bundle then includes that provider's SDK; `providers/all` includes all statically visible SDKs. Bedrock is the exception: its AWS SDK implementation is loaded through a bundler-opaque Node-only import.
|
|
1418
|
+
- Importing `@sammorrowdrums/mcpi-ai/api/<api-id>` directly loads that API implementation and its SDK immediately.
|
|
1419
|
+
|
|
1420
|
+
Avoid `@sammorrowdrums/mcpi-ai/compat` in new bundled apps; it preserves the old global API and imports the full built-in catalog surface.
|
|
1421
|
+
|
|
1422
|
+
For single-file Node ESM bundles, some SDK dependencies may still use dynamic CommonJS `require()` internally. If you see errors such as `Dynamic require of "child_process" is not supported`, add a Node `require` shim to the bundle. With esbuild:
|
|
1054
1423
|
|
|
1055
1424
|
```bash
|
|
1056
|
-
|
|
1425
|
+
esbuild app.js --bundle --platform=node --format=esm \
|
|
1426
|
+
--banner:js='import { createRequire } from "module";const require = createRequire(import.meta.url);' \
|
|
1427
|
+
--outfile=app.bundle.js
|
|
1057
1428
|
```
|
|
1058
1429
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
Set `PI_CACHE_RETENTION=long` to extend prompt cache retention:
|
|
1430
|
+
This is only for Node bundles; it is not a browser or Cloudflare Workers workaround.
|
|
1062
1431
|
|
|
1063
|
-
|
|
1064
|
-
|----------|---------|-------------------------------|
|
|
1065
|
-
| Anthropic | 5 minutes | 1 hour |
|
|
1066
|
-
| OpenAI | in-memory | 24 hours |
|
|
1432
|
+
Bedrock is Node-only. Add it like any other provider:
|
|
1067
1433
|
|
|
1068
|
-
|
|
1434
|
+
```typescript
|
|
1435
|
+
import { createModels } from '@sammorrowdrums/mcpi-ai';
|
|
1436
|
+
import { amazonBedrockProvider } from '@sammorrowdrums/mcpi-ai/providers/amazon-bedrock';
|
|
1069
1437
|
|
|
1070
|
-
|
|
1438
|
+
const models = createModels();
|
|
1439
|
+
models.setProvider(amazonBedrockProvider());
|
|
1440
|
+
```
|
|
1071
1441
|
|
|
1072
|
-
|
|
1442
|
+
In normal Node package usage and code-split bundles, Bedrock loads its AWS SDK implementation lazily. For a standalone single-file bundle that must include Bedrock support, register the implementation module explicitly:
|
|
1073
1443
|
|
|
1074
1444
|
```typescript
|
|
1075
|
-
import {
|
|
1445
|
+
import { setBedrockProviderModule } from '@sammorrowdrums/mcpi-ai/api/bedrock-converse-stream.lazy';
|
|
1446
|
+
import { bedrockProviderModule } from '@sammorrowdrums/mcpi-ai/bedrock-provider';
|
|
1447
|
+
|
|
1448
|
+
setBedrockProviderModule(bedrockProviderModule);
|
|
1449
|
+
```
|
|
1450
|
+
|
|
1451
|
+
That explicit override bundles the AWS SDK. Without it, Bedrock's opaque runtime import expects the package's Bedrock implementation file to be available at runtime.
|
|
1452
|
+
|
|
1453
|
+
### Provider-Scoped Environment Overrides
|
|
1076
1454
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1455
|
+
Pass `env` in stream options to scope provider configuration to a request. Values in `env` are used before process environment variables for provider auth and configuration such as Cloudflare account IDs, Azure OpenAI settings, Vertex project/location, Bedrock settings, `MCPI_CACHE_RETENTION`, and `HTTP_PROXY`/`HTTPS_PROXY`.
|
|
1456
|
+
|
|
1457
|
+
```typescript
|
|
1458
|
+
const models = builtinModels();
|
|
1459
|
+
const model = models.getModel('cloudflare-ai-gateway', 'workers-ai/@cf/moonshotai/kimi-k2.6')!;
|
|
1460
|
+
|
|
1461
|
+
const response = await models.complete(model, context, {
|
|
1462
|
+
env: {
|
|
1463
|
+
CLOUDFLARE_API_KEY: '...',
|
|
1464
|
+
CLOUDFLARE_ACCOUNT_ID: 'account-id',
|
|
1465
|
+
CLOUDFLARE_GATEWAY_ID: 'gateway-id'
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1079
1468
|
```
|
|
1080
1469
|
|
|
1470
|
+
Use this when one process needs different provider settings per request, or when ambient environment variables should not leak into a provider call.
|
|
1471
|
+
|
|
1081
1472
|
## OAuth Providers
|
|
1082
1473
|
|
|
1083
|
-
Several providers
|
|
1474
|
+
Several providers support OAuth authentication instead of static API keys:
|
|
1084
1475
|
|
|
1085
1476
|
- **Anthropic** (Claude Pro/Max subscription)
|
|
1086
1477
|
- **OpenAI Codex** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
|
|
1087
1478
|
- **GitHub Copilot** (Copilot subscription)
|
|
1088
|
-
- **
|
|
1089
|
-
|
|
1479
|
+
- **OpenRouter** (OAuth PKCE that mints a user-controlled API key)
|
|
1480
|
+
|
|
1481
|
+
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.
|
|
1482
|
+
|
|
1483
|
+
```typescript
|
|
1484
|
+
import { createModels } from '@sammorrowdrums/mcpi-ai';
|
|
1485
|
+
import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
|
|
1486
|
+
|
|
1487
|
+
const models = createModels({ credentials: myStore }); // persistent CredentialStore
|
|
1488
|
+
models.setProvider(anthropicProvider());
|
|
1489
|
+
|
|
1490
|
+
// Login: Models drives the flow and persists the credential
|
|
1491
|
+
await models.login('anthropic', 'oauth', {
|
|
1492
|
+
prompt: async (p) => {
|
|
1493
|
+
// p.type: 'text' | 'secret' | 'select' | 'manual_code'
|
|
1494
|
+
// manual_code prompts race a local callback server; p.signal aborts them when the server wins
|
|
1495
|
+
return await askUser(p.message);
|
|
1496
|
+
},
|
|
1497
|
+
notify: (event) => {
|
|
1498
|
+
// event.type: 'info' | 'auth_url' | 'device_code' | 'progress'
|
|
1499
|
+
if (event.type === 'info') {
|
|
1500
|
+
console.log(event.message);
|
|
1501
|
+
for (const link of event.links ?? []) console.log(`${link.label ?? 'More information'}: ${link.url}`);
|
|
1502
|
+
}
|
|
1503
|
+
if (event.type === 'auth_url') console.log(`Open: ${event.url}`);
|
|
1504
|
+
if (event.type === 'device_code') console.log(`Code: ${event.userCode} at ${event.verificationUri}`);
|
|
1505
|
+
if (event.type === 'progress') console.log(event.message);
|
|
1506
|
+
},
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1509
|
+
// From here on, requests resolve and refresh the token automatically
|
|
1510
|
+
const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
|
|
1511
|
+
await models.complete(model, context);
|
|
1090
1512
|
|
|
1091
|
-
|
|
1513
|
+
// Logout
|
|
1514
|
+
await models.logout('anthropic');
|
|
1515
|
+
```
|
|
1092
1516
|
|
|
1093
1517
|
### Vertex AI
|
|
1094
1518
|
|
|
1095
|
-
Vertex AI models support either a Google Cloud API key or Application Default Credentials (ADC):
|
|
1519
|
+
Vertex AI models support either a Google Cloud API key or Application Default Credentials (ADC). Its provider-owned API-key login flow can configure either method:
|
|
1096
1520
|
|
|
1097
1521
|
- **API key**: Set `GOOGLE_CLOUD_API_KEY` or pass `apiKey` in the call options.
|
|
1098
1522
|
- **Local development (ADC)**: Run `gcloud auth application-default login`
|
|
@@ -1100,8 +1524,6 @@ Vertex AI models support either a Google Cloud API key or Application Default Cr
|
|
|
1100
1524
|
|
|
1101
1525
|
When using ADC, also set `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) and `GOOGLE_CLOUD_LOCATION`. You can also pass `project`/`location` in the call options. When using `GOOGLE_CLOUD_API_KEY`, `project` and `location` are not required.
|
|
1102
1526
|
|
|
1103
|
-
Example:
|
|
1104
|
-
|
|
1105
1527
|
```bash
|
|
1106
1528
|
# Local (uses your user credentials)
|
|
1107
1529
|
gcloud auth application-default login
|
|
@@ -1112,23 +1534,6 @@ export GOOGLE_CLOUD_LOCATION="us-central1"
|
|
|
1112
1534
|
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
|
|
1113
1535
|
```
|
|
1114
1536
|
|
|
1115
|
-
```typescript
|
|
1116
|
-
import { getModel, complete } from '@mariozechner/pi-ai';
|
|
1117
|
-
|
|
1118
|
-
(async () => {
|
|
1119
|
-
const model = getModel('google-vertex', 'gemini-2.5-flash');
|
|
1120
|
-
const response = await complete(model, {
|
|
1121
|
-
messages: [{ role: 'user', content: 'Hello from Vertex AI' }]
|
|
1122
|
-
}, {
|
|
1123
|
-
apiKey: process.env.GOOGLE_CLOUD_API_KEY,
|
|
1124
|
-
});
|
|
1125
|
-
|
|
1126
|
-
for (const block of response.content) {
|
|
1127
|
-
if (block.type === 'text') console.log(block.text);
|
|
1128
|
-
}
|
|
1129
|
-
})().catch(console.error);
|
|
1130
|
-
```
|
|
1131
|
-
|
|
1132
1537
|
Official docs: [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials)
|
|
1133
1538
|
|
|
1134
1539
|
### CLI Login
|
|
@@ -1136,133 +1541,86 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a
|
|
|
1136
1541
|
The quickest way to authenticate:
|
|
1137
1542
|
|
|
1138
1543
|
```bash
|
|
1139
|
-
npx @
|
|
1140
|
-
npx @
|
|
1141
|
-
npx @
|
|
1544
|
+
npx @sammorrowdrums/mcpi-ai login # interactive provider selection
|
|
1545
|
+
npx @sammorrowdrums/mcpi-ai login anthropic # login to specific provider
|
|
1546
|
+
npx @sammorrowdrums/mcpi-ai list # list available providers
|
|
1142
1547
|
```
|
|
1143
1548
|
|
|
1144
1549
|
Credentials are saved to `auth.json` in the current directory.
|
|
1145
1550
|
|
|
1146
1551
|
### Programmatic OAuth
|
|
1147
1552
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
```typescript
|
|
1151
|
-
import {
|
|
1152
|
-
// Login functions (return credentials, do not store)
|
|
1153
|
-
loginAnthropic,
|
|
1154
|
-
loginOpenAICodex,
|
|
1155
|
-
loginGitHubCopilot,
|
|
1156
|
-
loginGeminiCli,
|
|
1157
|
-
loginAntigravity,
|
|
1158
|
-
|
|
1159
|
-
// Token management
|
|
1160
|
-
refreshOAuthToken, // (provider, credentials) => new credentials
|
|
1161
|
-
getOAuthApiKey, // (provider, credentialsMap) => { newCredentials, apiKey } | null
|
|
1162
|
-
|
|
1163
|
-
// Types
|
|
1164
|
-
type OAuthProvider, // 'anthropic' | 'openai-codex' | 'github-copilot' | 'google-gemini-cli' | 'google-antigravity'
|
|
1165
|
-
type OAuthCredentials,
|
|
1166
|
-
} from '@mariozechner/pi-ai/oauth';
|
|
1167
|
-
```
|
|
1553
|
+
Built-in login and refresh flows are private provider implementations. Use provider-owned `OAuthAuth`, which composes with `CredentialStore` and gets locked auto-refresh through `Models`. The `@sammorrowdrums/mcpi-ai/oauth` entry point retains only type declarations required by coding-agent extension OAuth compatibility.
|
|
1168
1554
|
|
|
1169
|
-
|
|
1555
|
+
Provider notes:
|
|
1170
1556
|
|
|
1171
|
-
|
|
1172
|
-
import { loginGitHubCopilot } from '@mariozechner/pi-ai/oauth';
|
|
1173
|
-
import { writeFileSync } from 'fs';
|
|
1557
|
+
**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.
|
|
1174
1558
|
|
|
1175
|
-
|
|
1176
|
-
onAuth: (url, instructions) => {
|
|
1177
|
-
console.log(`Open: ${url}`);
|
|
1178
|
-
if (instructions) console.log(instructions);
|
|
1179
|
-
},
|
|
1180
|
-
onPrompt: async (prompt) => {
|
|
1181
|
-
return await getUserInput(prompt.message);
|
|
1182
|
-
},
|
|
1183
|
-
onProgress: (message) => console.log(message)
|
|
1184
|
-
});
|
|
1559
|
+
**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.
|
|
1185
1560
|
|
|
1186
|
-
|
|
1187
|
-
const auth = { 'github-copilot': { type: 'oauth', ...credentials } };
|
|
1188
|
-
writeFileSync('auth.json', JSON.stringify(auth, null, 2));
|
|
1189
|
-
```
|
|
1561
|
+
**GitHub Copilot**: If you get "The requested model is not supported" error, enable the model manually in VS Code: open Copilot Chat, click the model selector, select the model (warning icon), and click "Enable".
|
|
1190
1562
|
|
|
1191
|
-
|
|
1563
|
+
## Migrating from the Old Global API
|
|
1192
1564
|
|
|
1193
|
-
|
|
1565
|
+
Older versions exposed a global API: `stream()`/`complete()` dispatching on `model.api` via a global registry, sync `getModel()`/`getModels()`/`getProviders()` catalog reads, `registerApiProvider()`, `getEnvApiKey()`, and per-API lazy stream functions. That surface lives unchanged on the **compat entrypoint**:
|
|
1194
1566
|
|
|
1195
1567
|
```typescript
|
|
1196
|
-
|
|
1197
|
-
import {
|
|
1198
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
1199
|
-
|
|
1200
|
-
// Load your stored credentials
|
|
1201
|
-
const auth = JSON.parse(readFileSync('auth.json', 'utf-8'));
|
|
1202
|
-
|
|
1203
|
-
// Get API key (refreshes if expired)
|
|
1204
|
-
const result = await getOAuthApiKey('github-copilot', auth);
|
|
1205
|
-
if (!result) throw new Error('Not logged in');
|
|
1206
|
-
|
|
1207
|
-
// Save refreshed credentials
|
|
1208
|
-
auth['github-copilot'] = { type: 'oauth', ...result.newCredentials };
|
|
1209
|
-
writeFileSync('auth.json', JSON.stringify(auth, null, 2));
|
|
1210
|
-
|
|
1211
|
-
// Use the API key
|
|
1212
|
-
const model = getModel('github-copilot', 'gpt-4o');
|
|
1213
|
-
const response = await complete(model, {
|
|
1214
|
-
messages: [{ role: 'user', content: 'Hello!' }]
|
|
1215
|
-
}, { apiKey: result.apiKey });
|
|
1216
|
-
```
|
|
1217
|
-
|
|
1218
|
-
### Provider Notes
|
|
1568
|
+
// Before
|
|
1569
|
+
import { getModel, complete } from '@sammorrowdrums/mcpi-ai';
|
|
1219
1570
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1571
|
+
// After (verbatim behavior, one import-path change)
|
|
1572
|
+
import { getModel, complete } from '@sammorrowdrums/mcpi-ai/compat';
|
|
1573
|
+
```
|
|
1223
1574
|
|
|
1224
|
-
|
|
1575
|
+
Compat is a strict superset of the root entrypoint, so a file can switch its import path wholesale. It will be removed in a future release; migrate to `createModels()` + provider factories:
|
|
1225
1576
|
|
|
1226
|
-
|
|
1577
|
+
| Old | New |
|
|
1578
|
+
|-----|-----|
|
|
1579
|
+
| `getModel('openai', 'gpt-4o-mini')` | `models.getModel('openai', 'gpt-4o-mini')` or `getBuiltinModel()` from `providers/all` |
|
|
1580
|
+
| `getModels('anthropic')` / `getProviders()` | `models.getModels('anthropic')` / `models.getProviders()` or `getBuiltin*` |
|
|
1581
|
+
| `stream(model, ctx, opts)` (env-key injection) | `models.stream(model, ctx, opts)` (provider auth resolution) |
|
|
1582
|
+
| `registerApiProvider({ api, stream, streamSimple })` | `createProvider({ id, auth, models, api })` + `models.setProvider()` |
|
|
1583
|
+
| `getEnvApiKey('openai')` | `await models.getAuth(model.provider)` |
|
|
1584
|
+
| `streamAnthropic(model, ctx, opts)` | `stream` from `@sammorrowdrums/mcpi-ai/api/anthropic-messages`, or a provider in a collection |
|
|
1585
|
+
| `registerFauxProvider()` | `fauxProvider()` + `models.setProvider()` |
|
|
1227
1586
|
|
|
1228
1587
|
## Development
|
|
1229
1588
|
|
|
1230
1589
|
### Adding a New Provider
|
|
1231
1590
|
|
|
1232
|
-
Adding a new LLM provider requires changes across multiple files. This checklist covers all necessary steps:
|
|
1591
|
+
Adding a new LLM provider requires changes across multiple files. The layered layout: API implementations live in `src/api/`, provider factories in `src/providers/`, stable generated catalog wrappers live in `src/providers/<id>.models.ts`, and `src/models.generated.ts` registers them. This checklist covers all necessary steps:
|
|
1233
1592
|
|
|
1234
1593
|
#### 1. Core Types (`src/types.ts`)
|
|
1235
1594
|
|
|
1236
|
-
- Add the API identifier to `KnownApi` (for example `"bedrock-converse-stream"`)
|
|
1237
|
-
- Create an options interface extending `StreamOptions` (for example `BedrockOptions`)
|
|
1595
|
+
- Add the API identifier to `KnownApi` (for example `"bedrock-converse-stream"`), if it is a new API
|
|
1238
1596
|
- Add the provider name to `KnownProvider` (for example `"amazon-bedrock"`)
|
|
1597
|
+
- Add the options type to `ApiOptionsMap`
|
|
1239
1598
|
|
|
1240
|
-
#### 2.
|
|
1599
|
+
#### 2. API Implementation (`src/api/<api-id>.ts`, only for a new API)
|
|
1241
1600
|
|
|
1242
|
-
Create a new
|
|
1601
|
+
Create a new API implementation file (for example `bedrock-converse-stream.ts`) that exports exactly `stream` and `streamSimple`, plus:
|
|
1243
1602
|
|
|
1244
|
-
- `
|
|
1245
|
-
- `streamSimple<Provider>()` for `SimpleStreamOptions` mapping
|
|
1246
|
-
- Provider-specific options interface
|
|
1603
|
+
- An options interface extending `StreamOptions` (for example `BedrockOptions`)
|
|
1247
1604
|
- Message conversion functions to transform `Context` to provider format
|
|
1248
1605
|
- Tool conversion if the provider supports tools
|
|
1249
1606
|
- Response parsing to emit standardized events (`text`, `tool_call`, `thinking`, `usage`, `stop`)
|
|
1250
1607
|
|
|
1251
|
-
|
|
1608
|
+
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 `@sammorrowdrums/mcpi-ai`.
|
|
1252
1609
|
|
|
1253
|
-
|
|
1254
|
-
- Add a package subpath export in `package.json` for the provider module (`./dist/providers/<provider>.js`)
|
|
1255
|
-
- Add lazy loader wrappers in `src/providers/register-builtins.ts`, do not statically import provider implementation modules there
|
|
1256
|
-
- Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@mariozechner/pi-ai`
|
|
1257
|
-
- Add credential detection in `env-api-keys.ts` for the new provider
|
|
1258
|
-
- Ensure `streamSimple` handles auth lookup via `getEnvApiKey()` or provider-specific auth
|
|
1259
|
-
|
|
1260
|
-
#### 4. Model Generation (`scripts/generate-models.ts`)
|
|
1610
|
+
#### 3. Model Generation (`scripts/generate-models.ts`, `scripts/generate-image-models.ts`)
|
|
1261
1611
|
|
|
1262
1612
|
- Add logic to fetch and parse models from the provider's source (e.g., models.dev API)
|
|
1263
|
-
- Map provider model data to the standardized `Model` interface
|
|
1613
|
+
- Map chat/tool-capable provider model data to the standardized `Model` interface via `scripts/generate-models.ts`; 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
|
|
1614
|
+
- Map image-generation provider model data to the standardized `ImagesModel` interface via `scripts/generate-image-models.ts`
|
|
1264
1615
|
- Handle provider-specific quirks (pricing format, capability flags, model ID transformations)
|
|
1265
1616
|
|
|
1617
|
+
#### 4. Provider Factory (`src/providers/<id>.ts`)
|
|
1618
|
+
|
|
1619
|
+
- `createProvider()` wiring catalog + auth + the lazy API wrapper
|
|
1620
|
+
- Auth: `envApiKeyAuth` for standard key providers, a custom `ApiKeyAuth` for ambient auth (AWS profiles, ADC), `lazyOAuth` where an OAuth flow exists
|
|
1621
|
+
- Register the factory in `src/providers/all.ts`
|
|
1622
|
+
- If it is a new API: register it in the builtin list in `src/compat.ts` and add the package subpath export in `package.json`
|
|
1623
|
+
|
|
1266
1624
|
#### 5. Tests (`test/`)
|
|
1267
1625
|
|
|
1268
1626
|
Create or update test files to cover the new provider:
|
|
@@ -1278,6 +1636,7 @@ Create or update test files to cover the new provider:
|
|
|
1278
1636
|
- `image-tool-result.test.ts` - Images in tool results
|
|
1279
1637
|
- `total-tokens.test.ts` - Token counting accuracy
|
|
1280
1638
|
- `cross-provider-handoff.test.ts` - Cross-provider context replay
|
|
1639
|
+
- `providers.test.ts` - Provider listing and auth resolution
|
|
1281
1640
|
|
|
1282
1641
|
For `cross-provider-handoff.test.ts`, add at least one provider/model pair. If the provider exposes multiple model families (for example GPT and Claude), add at least one pair per family.
|
|
1283
1642
|
|