pi-ai-python 1.3.0__py3-none-any.whl

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.
Files changed (189) hide show
  1. pi_agent/__init__.py +127 -0
  2. pi_agent/agent.py +617 -0
  3. pi_agent/agent_loop.py +820 -0
  4. pi_agent/proxy.py +269 -0
  5. pi_agent/py.typed +0 -0
  6. pi_agent/stream_fn.py +27 -0
  7. pi_agent/tools.py +327 -0
  8. pi_agent/types.py +394 -0
  9. pi_ai/__init__.py +263 -0
  10. pi_ai/abort.py +315 -0
  11. pi_ai/api/__init__.py +1 -0
  12. pi_ai/api/anthropic_messages.py +1782 -0
  13. pi_ai/api/azure_openai_config.py +138 -0
  14. pi_ai/api/azure_openai_responses.py +245 -0
  15. pi_ai/api/bedrock_converse_stream.py +1598 -0
  16. pi_ai/api/classifier_shared.py +211 -0
  17. pi_ai/api/cloudflare.py +36 -0
  18. pi_ai/api/cloudflare_workers_ai_system_one.py +80 -0
  19. pi_ai/api/constrained_sampling.py +279 -0
  20. pi_ai/api/github_copilot_headers.py +30 -0
  21. pi_ai/api/google_generative_ai.py +138 -0
  22. pi_ai/api/google_shared.py +927 -0
  23. pi_ai/api/google_vertex.py +234 -0
  24. pi_ai/api/lazy.py +156 -0
  25. pi_ai/api/llama_cpp_classify.py +449 -0
  26. pi_ai/api/mistral_conversations.py +756 -0
  27. pi_ai/api/openai_codex_responses.py +1545 -0
  28. pi_ai/api/openai_completions.py +1594 -0
  29. pi_ai/api/openai_decisions.py +210 -0
  30. pi_ai/api/openai_prompt_cache.py +19 -0
  31. pi_ai/api/openai_responses.py +418 -0
  32. pi_ai/api/openai_responses_shared.py +840 -0
  33. pi_ai/api/openai_sdk.py +204 -0
  34. pi_ai/api/openrouter_images.py +153 -0
  35. pi_ai/api/pi_messages.py +449 -0
  36. pi_ai/api/registry.py +103 -0
  37. pi_ai/api/simple_options.py +119 -0
  38. pi_ai/api/system_one_shared.py +148 -0
  39. pi_ai/api/transform_messages.py +211 -0
  40. pi_ai/api/typesafe_system_one.py +43 -0
  41. pi_ai/auth/__init__.py +1 -0
  42. pi_ai/auth/context.py +47 -0
  43. pi_ai/auth/credential_store.py +208 -0
  44. pi_ai/auth/helpers.py +123 -0
  45. pi_ai/auth/oauth/__init__.py +5 -0
  46. pi_ai/auth/oauth/_common.py +454 -0
  47. pi_ai/auth/oauth/anthropic.py +283 -0
  48. pi_ai/auth/oauth/callback_server.py +339 -0
  49. pi_ai/auth/oauth/device_code.py +127 -0
  50. pi_ai/auth/oauth/github_copilot.py +508 -0
  51. pi_ai/auth/oauth/kimi_coding.py +283 -0
  52. pi_ai/auth/oauth/load.py +64 -0
  53. pi_ai/auth/oauth/meta.py +238 -0
  54. pi_ai/auth/oauth/openai_chatgpt.py +321 -0
  55. pi_ai/auth/oauth/openai_codex.py +391 -0
  56. pi_ai/auth/oauth/openrouter.py +173 -0
  57. pi_ai/auth/oauth/pkce.py +23 -0
  58. pi_ai/auth/oauth/radius.py +311 -0
  59. pi_ai/auth/oauth/xai.py +220 -0
  60. pi_ai/auth/resolve.py +190 -0
  61. pi_ai/auth/types.py +292 -0
  62. pi_ai/cli.py +265 -0
  63. pi_ai/compat.py +651 -0
  64. pi_ai/env_api_keys.py +137 -0
  65. pi_ai/event_stream.py +329 -0
  66. pi_ai/models.py +1191 -0
  67. pi_ai/models_store.py +121 -0
  68. pi_ai/oauth.py +90 -0
  69. pi_ai/providers/__init__.py +1 -0
  70. pi_ai/providers/all.py +154 -0
  71. pi_ai/providers/amazon_bedrock.py +116 -0
  72. pi_ai/providers/ant_ling.py +23 -0
  73. pi_ai/providers/anthropic.py +105 -0
  74. pi_ai/providers/azure.py +103 -0
  75. pi_ai/providers/baseten.py +23 -0
  76. pi_ai/providers/catalog.py +167 -0
  77. pi_ai/providers/cerebras.py +23 -0
  78. pi_ai/providers/cloudflare_ai_gateway.py +30 -0
  79. pi_ai/providers/cloudflare_auth.py +112 -0
  80. pi_ai/providers/cloudflare_stream.py +84 -0
  81. pi_ai/providers/cloudflare_workers_ai.py +26 -0
  82. pi_ai/providers/data/amazon-bedrock.json +1 -0
  83. pi_ai/providers/data/ant-ling.json +1 -0
  84. pi_ai/providers/data/anthropic.json +1 -0
  85. pi_ai/providers/data/azure.json +1 -0
  86. pi_ai/providers/data/baseten.json +1 -0
  87. pi_ai/providers/data/cerebras.json +1 -0
  88. pi_ai/providers/data/cloudflare-ai-gateway.json +1 -0
  89. pi_ai/providers/data/cloudflare-workers-ai.json +1 -0
  90. pi_ai/providers/data/deepseek.json +1 -0
  91. pi_ai/providers/data/fireworks.json +1 -0
  92. pi_ai/providers/data/github-copilot.json +1 -0
  93. pi_ai/providers/data/google-vertex.json +1 -0
  94. pi_ai/providers/data/google.json +1 -0
  95. pi_ai/providers/data/groq.json +1 -0
  96. pi_ai/providers/data/huggingface.json +1 -0
  97. pi_ai/providers/data/kimi-coding.json +1 -0
  98. pi_ai/providers/data/manifest.json +1 -0
  99. pi_ai/providers/data/meta.json +1 -0
  100. pi_ai/providers/data/minimax-cn.json +1 -0
  101. pi_ai/providers/data/minimax.json +1 -0
  102. pi_ai/providers/data/mistral.json +1 -0
  103. pi_ai/providers/data/moonshotai-cn.json +1 -0
  104. pi_ai/providers/data/moonshotai.json +1 -0
  105. pi_ai/providers/data/nvidia.json +1 -0
  106. pi_ai/providers/data/openai-codex.json +1 -0
  107. pi_ai/providers/data/openai.json +1 -0
  108. pi_ai/providers/data/opencode-go.json +1 -0
  109. pi_ai/providers/data/opencode.json +1 -0
  110. pi_ai/providers/data/openrouter.json +1 -0
  111. pi_ai/providers/data/qwen-token-plan-cn.json +1 -0
  112. pi_ai/providers/data/qwen-token-plan-individual.json +1 -0
  113. pi_ai/providers/data/qwen-token-plan.json +1 -0
  114. pi_ai/providers/data/radius.json +1 -0
  115. pi_ai/providers/data/together.json +1 -0
  116. pi_ai/providers/data/typesafe.json +1 -0
  117. pi_ai/providers/data/vercel-ai-gateway.json +1 -0
  118. pi_ai/providers/data/xai.json +1 -0
  119. pi_ai/providers/data/xiaomi-token-plan-ams.json +1 -0
  120. pi_ai/providers/data/xiaomi-token-plan-cn.json +1 -0
  121. pi_ai/providers/data/xiaomi-token-plan-sgp.json +1 -0
  122. pi_ai/providers/data/xiaomi.json +1 -0
  123. pi_ai/providers/data/zai-coding-cn.json +1 -0
  124. pi_ai/providers/data/zai.json +1 -0
  125. pi_ai/providers/deepseek.py +23 -0
  126. pi_ai/providers/faux.py +680 -0
  127. pi_ai/providers/fireworks.py +26 -0
  128. pi_ai/providers/github_copilot.py +46 -0
  129. pi_ai/providers/google.py +23 -0
  130. pi_ai/providers/google_vertex.py +131 -0
  131. pi_ai/providers/groq.py +23 -0
  132. pi_ai/providers/huggingface.py +23 -0
  133. pi_ai/providers/kimi_coding.py +32 -0
  134. pi_ai/providers/meta.py +32 -0
  135. pi_ai/providers/minimax.py +23 -0
  136. pi_ai/providers/minimax_cn.py +23 -0
  137. pi_ai/providers/mistral.py +23 -0
  138. pi_ai/providers/moonshotai.py +23 -0
  139. pi_ai/providers/moonshotai_cn.py +23 -0
  140. pi_ai/providers/nvidia.py +23 -0
  141. pi_ai/providers/openai.py +44 -0
  142. pi_ai/providers/openai_codex.py +26 -0
  143. pi_ai/providers/opencode.py +36 -0
  144. pi_ai/providers/opencode_go.py +33 -0
  145. pi_ai/providers/opencode_headers.py +60 -0
  146. pi_ai/providers/openrouter.py +145 -0
  147. pi_ai/providers/openrouter_api.py +122 -0
  148. pi_ai/providers/qwen_token_plan.py +23 -0
  149. pi_ai/providers/qwen_token_plan_cn.py +23 -0
  150. pi_ai/providers/qwen_token_plan_individual.py +23 -0
  151. pi_ai/providers/radius.py +146 -0
  152. pi_ai/providers/radius_config.py +136 -0
  153. pi_ai/providers/together.py +23 -0
  154. pi_ai/providers/typesafe.py +22 -0
  155. pi_ai/providers/vercel_ai_gateway.py +25 -0
  156. pi_ai/providers/xai.py +32 -0
  157. pi_ai/providers/xiaomi.py +23 -0
  158. pi_ai/providers/xiaomi_token_plan_ams.py +23 -0
  159. pi_ai/providers/xiaomi_token_plan_cn.py +23 -0
  160. pi_ai/providers/xiaomi_token_plan_sgp.py +23 -0
  161. pi_ai/providers/zai.py +23 -0
  162. pi_ai/providers/zai_coding_cn.py +23 -0
  163. pi_ai/py.typed +0 -0
  164. pi_ai/serialization.py +477 -0
  165. pi_ai/session_resources.py +33 -0
  166. pi_ai/types.py +1003 -0
  167. pi_ai/utils/__init__.py +1 -0
  168. pi_ai/utils/callbacks.py +15 -0
  169. pi_ai/utils/diagnostics.py +70 -0
  170. pi_ai/utils/error_body.py +89 -0
  171. pi_ai/utils/estimate.py +125 -0
  172. pi_ai/utils/fastjson.py +63 -0
  173. pi_ai/utils/http.py +409 -0
  174. pi_ai/utils/json_parse.py +424 -0
  175. pi_ai/utils/misc.py +193 -0
  176. pi_ai/utils/model_operations.py +73 -0
  177. pi_ai/utils/models_error.py +35 -0
  178. pi_ai/utils/oauth_page.py +121 -0
  179. pi_ai/utils/overflow.py +87 -0
  180. pi_ai/utils/retry.py +193 -0
  181. pi_ai/utils/schema.py +283 -0
  182. pi_ai/utils/text.py +65 -0
  183. pi_ai/utils/transcript.py +271 -0
  184. pi_ai/utils/validation.py +813 -0
  185. pi_ai_python-1.3.0.dist-info/METADATA +167 -0
  186. pi_ai_python-1.3.0.dist-info/RECORD +189 -0
  187. pi_ai_python-1.3.0.dist-info/WHEEL +4 -0
  188. pi_ai_python-1.3.0.dist-info/entry_points.txt +2 -0
  189. pi_ai_python-1.3.0.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,46 @@
1
+ """GitHub Copilot provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from ..api.registry import anthropic_messages_api, openai_completions_api, openai_responses_api
8
+ from ..auth.helpers import env_api_key_auth, lazy_oauth
9
+ from ..auth.oauth.load import load_github_copilot_oauth
10
+ from ..auth.types import Credential, ProviderAuth
11
+ from ..models import Provider, create_provider
12
+ from ..types import Model
13
+ from .catalog import catalog_models
14
+
15
+ __all__ = ["github_copilot_provider"]
16
+
17
+
18
+ def _filter_models(models: Sequence[Model], credential: Credential | None) -> Sequence[Model]:
19
+ # OAuth credentials record the models the Copilot plan enables.
20
+ if credential is None or credential.get("type") != "oauth":
21
+ return models
22
+ available_model_ids = credential.get("availableModelIds")
23
+ if not isinstance(available_model_ids, list) or not all(isinstance(item, str) for item in available_model_ids):
24
+ return models
25
+ available = set(available_model_ids)
26
+ return [model for model in models if model.id in available]
27
+
28
+
29
+ def github_copilot_provider() -> Provider:
30
+ """GitHub Copilot: ``COPILOT_GITHUB_TOKEN`` or a GitHub Copilot login (Anthropic/OpenAI APIs)."""
31
+ return create_provider(
32
+ id="github-copilot",
33
+ name="GitHub Copilot",
34
+ base_url="https://api.individual.githubcopilot.com",
35
+ auth=ProviderAuth(
36
+ api_key=env_api_key_auth("GitHub Copilot token", ["COPILOT_GITHUB_TOKEN"]),
37
+ oauth=lazy_oauth("GitHub Copilot", load_github_copilot_oauth, is_subscription=True),
38
+ ),
39
+ models=lambda: catalog_models("github-copilot"),
40
+ filter_models=_filter_models,
41
+ api={
42
+ "anthropic-messages": anthropic_messages_api(),
43
+ "openai-completions": openai_completions_api(),
44
+ "openai-responses": openai_responses_api(),
45
+ },
46
+ )
@@ -0,0 +1,23 @@
1
+ """Google provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import google_generative_ai_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["google_provider"]
12
+
13
+
14
+ def google_provider() -> Provider:
15
+ """Google (Gemini API), authenticated with ``GEMINI_API_KEY``."""
16
+ return create_provider(
17
+ id="google",
18
+ name="Google",
19
+ base_url="https://generativelanguage.googleapis.com/v1beta",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Gemini API key", ["GEMINI_API_KEY"])),
21
+ models=lambda: catalog_models("google"),
22
+ api=google_generative_ai_api(),
23
+ )
@@ -0,0 +1,131 @@
1
+ """Google Vertex AI provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..abort import AbortSignal
6
+ from ..api.registry import google_vertex_api
7
+ from ..auth.types import (
8
+ ApiKeyAuth,
9
+ AuthContext,
10
+ AuthEvent,
11
+ AuthInfoLink,
12
+ AuthPrompt,
13
+ AuthResult,
14
+ AuthSelectOption,
15
+ Credential,
16
+ ModelAuth,
17
+ ProviderAuth,
18
+ ProviderAuthInteraction,
19
+ )
20
+ from ..models import Provider, create_provider
21
+ from .catalog import catalog_models
22
+
23
+ __all__ = ["google_vertex_provider", "vertex_auth", "VERTEX_ADC_PATH"]
24
+
25
+ VERTEX_ADC_PATH = "~/.config/gcloud/application_default_credentials.json"
26
+
27
+
28
+ async def _login(interaction: ProviderAuthInteraction) -> Credential:
29
+ interaction.signal.throw_if_aborted()
30
+ method = await interaction.prompt(
31
+ AuthPrompt(
32
+ type="select",
33
+ message="Select Google Vertex AI authentication method:",
34
+ options=[
35
+ AuthSelectOption(id="api-key", label="Google Cloud API key"),
36
+ AuthSelectOption(id="adc", label="Application Default Credentials"),
37
+ AuthSelectOption(id="service-account", label="Service account credentials file"),
38
+ ],
39
+ )
40
+ )
41
+ interaction.signal.throw_if_aborted()
42
+ if method == "api-key":
43
+ return {
44
+ "type": "api_key",
45
+ "key": await interaction.prompt(AuthPrompt(type="secret", message="Enter Google Cloud API key")),
46
+ }
47
+ if method not in ("adc", "service-account"):
48
+ raise ValueError(f"Unknown Google Vertex AI auth method: {method}")
49
+ interaction.notify(
50
+ AuthEvent(
51
+ type="info",
52
+ message=(
53
+ "Run `gcloud auth application-default login`, then provide the project and location."
54
+ if method == "adc"
55
+ else "Provide a service account credentials file, project, and location."
56
+ ),
57
+ links=[
58
+ AuthInfoLink(
59
+ label="Application Default Credentials",
60
+ url="https://cloud.google.com/docs/authentication/provide-credentials-adc",
61
+ )
62
+ ],
63
+ )
64
+ )
65
+ project = await interaction.prompt(AuthPrompt(type="text", message="Enter Google Cloud project ID"))
66
+ location = await interaction.prompt(AuthPrompt(type="text", message="Enter Google Cloud location"))
67
+ credentials_path = (
68
+ await interaction.prompt(AuthPrompt(type="text", message="Enter service account credentials file path"))
69
+ if method == "service-account"
70
+ else None
71
+ )
72
+ env = {"GOOGLE_CLOUD_PROJECT": project, "GOOGLE_CLOUD_LOCATION": location}
73
+ if credentials_path:
74
+ env["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path
75
+ return {"type": "api_key", "env": env}
76
+
77
+
78
+ async def _resolve(*, ctx: AuthContext, credential: Credential | None, signal: AbortSignal) -> AuthResult | None:
79
+ async def env(name: str) -> str | None:
80
+ signal.throw_if_aborted()
81
+ value = await ctx.env(name)
82
+ signal.throw_if_aborted()
83
+ return value
84
+
85
+ stored_key = credential.get("key") if credential else None
86
+ key = stored_key if stored_key is not None else await env("GOOGLE_CLOUD_API_KEY")
87
+ if key:
88
+ return AuthResult(ModelAuth(api_key=key), source="stored credential" if stored_key else "GOOGLE_CLOUD_API_KEY")
89
+
90
+ credential_env = (credential.get("env") if credential else None) or {}
91
+ adc_path = credential_env.get("GOOGLE_APPLICATION_CREDENTIALS")
92
+ if adc_path is None:
93
+ adc_path = await env("GOOGLE_APPLICATION_CREDENTIALS")
94
+ signal.throw_if_aborted()
95
+ has_credentials = await ctx.file_exists(adc_path if adc_path is not None else VERTEX_ADC_PATH)
96
+ signal.throw_if_aborted()
97
+ project = credential_env.get("GOOGLE_CLOUD_PROJECT")
98
+ if project is None:
99
+ project = await env("GOOGLE_CLOUD_PROJECT")
100
+ if project is None:
101
+ project = await env("GCLOUD_PROJECT")
102
+ location = credential_env.get("GOOGLE_CLOUD_LOCATION")
103
+ if location is None:
104
+ location = await env("GOOGLE_CLOUD_LOCATION")
105
+ if has_credentials and project and location:
106
+ return AuthResult(
107
+ ModelAuth(),
108
+ env=credential.get("env") if credential else None,
109
+ source="stored credential" if credential else "gcloud application default credentials",
110
+ )
111
+ return None
112
+
113
+
114
+ def vertex_auth() -> ApiKeyAuth:
115
+ """Vertex accepts an explicit API key or Application Default Credentials.
116
+
117
+ ADC (``gcloud auth application-default login``) additionally requires project
118
+ and location env vars, which the API implementation reads itself.
119
+ """
120
+ return ApiKeyAuth("Google Cloud credentials", _resolve, login=_login)
121
+
122
+
123
+ def google_vertex_provider() -> Provider:
124
+ """Google Vertex AI with ``GOOGLE_CLOUD_API_KEY`` or Application Default Credentials."""
125
+ return create_provider(
126
+ id="google-vertex",
127
+ name="Google Vertex AI",
128
+ auth=ProviderAuth(api_key=vertex_auth()),
129
+ models=lambda: catalog_models("google-vertex"),
130
+ api=google_vertex_api(),
131
+ )
@@ -0,0 +1,23 @@
1
+ """Groq provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["groq_provider"]
12
+
13
+
14
+ def groq_provider() -> Provider:
15
+ """Groq (OpenAI-compatible Chat Completions), authenticated with ``GROQ_API_KEY``."""
16
+ return create_provider(
17
+ id="groq",
18
+ name="Groq",
19
+ base_url="https://api.groq.com/openai/v1",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Groq API key", ["GROQ_API_KEY"])),
21
+ models=lambda: catalog_models("groq"),
22
+ api=openai_completions_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """Hugging Face provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["huggingface_provider"]
12
+
13
+
14
+ def huggingface_provider() -> Provider:
15
+ """Hugging Face (OpenAI-compatible Chat Completions), authenticated with ``HF_TOKEN``."""
16
+ return create_provider(
17
+ id="huggingface",
18
+ name="Hugging Face",
19
+ base_url="https://router.huggingface.co/v1",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Hugging Face token", ["HF_TOKEN"])),
21
+ models=lambda: catalog_models("huggingface"),
22
+ api=openai_completions_api(),
23
+ )
@@ -0,0 +1,32 @@
1
+ """Kimi For Coding provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth, lazy_oauth
7
+ from ..auth.oauth.load import load_kimi_coding_oauth
8
+ from ..auth.types import ProviderAuth
9
+ from ..models import Provider, create_provider
10
+ from .catalog import catalog_models
11
+
12
+ __all__ = ["kimi_coding_provider"]
13
+
14
+
15
+ def kimi_coding_provider() -> Provider:
16
+ """Kimi For Coding: ``KIMI_API_KEY`` or a Kimi Code subscription login."""
17
+ return create_provider(
18
+ id="kimi-coding",
19
+ name="Kimi For Coding",
20
+ base_url="https://api.kimi.com/coding/v1",
21
+ auth=ProviderAuth(
22
+ api_key=env_api_key_auth("Kimi API key", ["KIMI_API_KEY"]),
23
+ oauth=lazy_oauth(
24
+ "Kimi Code (subscription)",
25
+ load_kimi_coding_oauth,
26
+ is_subscription=True,
27
+ login_label="Sign in with Kimi Code",
28
+ ),
29
+ ),
30
+ models=lambda: catalog_models("kimi-coding"),
31
+ api=openai_completions_api(),
32
+ )
@@ -0,0 +1,32 @@
1
+ """Meta provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_responses_api
6
+ from ..auth.helpers import env_api_key_auth, lazy_oauth
7
+ from ..auth.oauth.load import load_meta_oauth
8
+ from ..auth.types import ProviderAuth
9
+ from ..models import Provider, create_provider
10
+ from .catalog import catalog_models
11
+
12
+ __all__ = ["meta_provider"]
13
+
14
+
15
+ def meta_provider() -> Provider:
16
+ """Meta (OpenAI Responses API): ``META_API_KEY`` or a Muse subscription login."""
17
+ return create_provider(
18
+ id="meta",
19
+ name="Meta",
20
+ base_url="https://api.meta.ai/v1",
21
+ auth=ProviderAuth(
22
+ api_key=env_api_key_auth("Meta Model API key", ["META_API_KEY"]),
23
+ oauth=lazy_oauth(
24
+ "Meta (Muse subscription)",
25
+ load_meta_oauth,
26
+ is_subscription=True,
27
+ login_label="Sign in with Meta",
28
+ ),
29
+ ),
30
+ models=lambda: catalog_models("meta"),
31
+ api=openai_responses_api(),
32
+ )
@@ -0,0 +1,23 @@
1
+ """MiniMax provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import anthropic_messages_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["minimax_provider"]
12
+
13
+
14
+ def minimax_provider() -> Provider:
15
+ """MiniMax (Anthropic Messages API), authenticated with ``MINIMAX_API_KEY``."""
16
+ return create_provider(
17
+ id="minimax",
18
+ name="MiniMax",
19
+ base_url="https://api.minimax.io/anthropic",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("MiniMax API key", ["MINIMAX_API_KEY"])),
21
+ models=lambda: catalog_models("minimax"),
22
+ api=anthropic_messages_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """MiniMax CN provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import anthropic_messages_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["minimax_cn_provider"]
12
+
13
+
14
+ def minimax_cn_provider() -> Provider:
15
+ """MiniMax CN (Anthropic Messages API), authenticated with ``MINIMAX_CN_API_KEY``."""
16
+ return create_provider(
17
+ id="minimax-cn",
18
+ name="MiniMax CN",
19
+ base_url="https://api.minimaxi.com/anthropic",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("MiniMax CN API key", ["MINIMAX_CN_API_KEY"])),
21
+ models=lambda: catalog_models("minimax-cn"),
22
+ api=anthropic_messages_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """Mistral provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import mistral_conversations_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["mistral_provider"]
12
+
13
+
14
+ def mistral_provider() -> Provider:
15
+ """Mistral (Mistral Conversations API), authenticated with ``MISTRAL_API_KEY``."""
16
+ return create_provider(
17
+ id="mistral",
18
+ name="Mistral",
19
+ base_url="https://api.mistral.ai",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Mistral API key", ["MISTRAL_API_KEY"])),
21
+ models=lambda: catalog_models("mistral"),
22
+ api=mistral_conversations_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """Moonshot AI provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["moonshotai_provider"]
12
+
13
+
14
+ def moonshotai_provider() -> Provider:
15
+ """Moonshot AI (OpenAI-compatible Chat Completions), authenticated with ``MOONSHOT_API_KEY``."""
16
+ return create_provider(
17
+ id="moonshotai",
18
+ name="Moonshot AI",
19
+ base_url="https://api.moonshot.ai/v1",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Moonshot AI API key", ["MOONSHOT_API_KEY"])),
21
+ models=lambda: catalog_models("moonshotai"),
22
+ api=openai_completions_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """Moonshot AI CN provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["moonshotai_cn_provider"]
12
+
13
+
14
+ def moonshotai_cn_provider() -> Provider:
15
+ """Moonshot AI CN (OpenAI-compatible Chat Completions), authenticated with ``MOONSHOT_API_KEY``."""
16
+ return create_provider(
17
+ id="moonshotai-cn",
18
+ name="Moonshot AI CN",
19
+ base_url="https://api.moonshot.cn/v1",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("Moonshot AI API key", ["MOONSHOT_API_KEY"])),
21
+ models=lambda: catalog_models("moonshotai-cn"),
22
+ api=openai_completions_api(),
23
+ )
@@ -0,0 +1,23 @@
1
+ """NVIDIA provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_completions_api
6
+ from ..auth.helpers import env_api_key_auth
7
+ from ..auth.types import ProviderAuth
8
+ from ..models import Provider, create_provider
9
+ from .catalog import catalog_models
10
+
11
+ __all__ = ["nvidia_provider"]
12
+
13
+
14
+ def nvidia_provider() -> Provider:
15
+ """NVIDIA (OpenAI-compatible Chat Completions), authenticated with ``NVIDIA_API_KEY``."""
16
+ return create_provider(
17
+ id="nvidia",
18
+ name="NVIDIA",
19
+ base_url="https://integrate.api.nvidia.com/v1",
20
+ auth=ProviderAuth(api_key=env_api_key_auth("NVIDIA API key", ["NVIDIA_API_KEY"])),
21
+ models=lambda: catalog_models("nvidia"),
22
+ api=openai_completions_api(),
23
+ )
@@ -0,0 +1,44 @@
1
+ """OpenAI provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from ..api.registry import openai_decisions_api, openai_responses_api
8
+ from ..auth.helpers import env_api_key_auth, lazy_oauth
9
+ from ..auth.oauth.load import load_openai_chatgpt_oauth
10
+ from ..auth.types import Credential, ProviderAuth
11
+ from ..models import Provider, create_provider, is_model_type
12
+ from ..types import AnyModel
13
+ from .catalog import catalog_models
14
+
15
+ __all__ = ["openai_provider"]
16
+
17
+
18
+ def _filter_all_models(models: Sequence[AnyModel], credential: Credential | None) -> Sequence[AnyModel]:
19
+ # Sign in with ChatGPT tokens only reach the Responses API; the Decisions API rejects them.
20
+ if credential is not None and credential.get("type") == "oauth":
21
+ return [model for model in models if not is_model_type(model, "classifier")]
22
+ return models
23
+
24
+
25
+ def openai_provider() -> Provider:
26
+ """OpenAI (Responses API plus Decisions classifiers): ``OPENAI_API_KEY`` or Sign in with ChatGPT."""
27
+ return create_provider(
28
+ id="openai",
29
+ name="OpenAI",
30
+ base_url="https://api.openai.com/v1",
31
+ auth=ProviderAuth(
32
+ api_key=env_api_key_auth("OpenAI API key", ["OPENAI_API_KEY"]),
33
+ oauth=lazy_oauth(
34
+ "OpenAI (ChatGPT subscription)",
35
+ load_openai_chatgpt_oauth,
36
+ is_subscription=True,
37
+ login_label="Sign in with ChatGPT",
38
+ ),
39
+ ),
40
+ models=lambda: catalog_models("openai"),
41
+ filter_all_models=_filter_all_models,
42
+ api=openai_responses_api(),
43
+ classifiers={"openai-decisions": openai_decisions_api()},
44
+ )
@@ -0,0 +1,26 @@
1
+ """OpenAI Codex (legacy ChatGPT backend) provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import openai_codex_responses_api
6
+ from ..auth.helpers import lazy_oauth
7
+ from ..auth.oauth.load import load_openai_codex_oauth
8
+ from ..auth.types import ProviderAuth
9
+ from ..models import Provider, create_provider
10
+ from .catalog import catalog_models
11
+
12
+ __all__ = ["openai_codex_provider"]
13
+
14
+
15
+ def openai_codex_provider() -> Provider:
16
+ """OpenAI Codex through the ChatGPT backend; OAuth (ChatGPT Plus/Pro) only."""
17
+ return create_provider(
18
+ id="openai-codex",
19
+ name="OpenAI Codex (legacy)",
20
+ base_url="https://chatgpt.com/backend-api",
21
+ auth=ProviderAuth(
22
+ oauth=lazy_oauth("OpenAI (ChatGPT Plus/Pro)", load_openai_codex_oauth, is_subscription=True),
23
+ ),
24
+ models=lambda: catalog_models("openai-codex"),
25
+ api=openai_codex_responses_api(),
26
+ )
@@ -0,0 +1,36 @@
1
+ """OpenCode Zen provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import (
6
+ anthropic_messages_api,
7
+ google_generative_ai_api,
8
+ openai_completions_api,
9
+ openai_responses_api,
10
+ typesafe_system_one_api,
11
+ )
12
+ from ..auth.helpers import env_api_key_auth
13
+ from ..auth.types import ProviderAuth
14
+ from ..models import Provider, create_provider
15
+ from .catalog import catalog_models
16
+ from .opencode_headers import with_opencode_session_header
17
+
18
+ __all__ = ["opencode_provider"]
19
+
20
+
21
+ def opencode_provider() -> Provider:
22
+ """OpenCode Zen (mixed APIs), authenticated with ``OPENCODE_API_KEY``."""
23
+ return create_provider(
24
+ id="opencode",
25
+ name="OpenCode Zen",
26
+ auth=ProviderAuth(api_key=env_api_key_auth("OpenCode API key", ["OPENCODE_API_KEY"])),
27
+ models=lambda: catalog_models("opencode"),
28
+ api={
29
+ "anthropic-messages": with_opencode_session_header(anthropic_messages_api()),
30
+ "google-generative-ai": with_opencode_session_header(google_generative_ai_api()),
31
+ "openai-completions": with_opencode_session_header(openai_completions_api()),
32
+ "openai-responses": with_opencode_session_header(openai_responses_api()),
33
+ },
34
+ # OpenCode Zen serves TypeSafe's System One protocol at /zen/v1/systemone.
35
+ classifiers={"typesafe-system-one": typesafe_system_one_api()},
36
+ )
@@ -0,0 +1,33 @@
1
+ """OpenCode Go provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..api.registry import (
6
+ anthropic_messages_api,
7
+ google_generative_ai_api,
8
+ openai_completions_api,
9
+ openai_responses_api,
10
+ )
11
+ from ..auth.helpers import env_api_key_auth
12
+ from ..auth.types import ProviderAuth
13
+ from ..models import Provider, create_provider
14
+ from .catalog import catalog_models
15
+ from .opencode_headers import with_opencode_session_header
16
+
17
+ __all__ = ["opencode_go_provider"]
18
+
19
+
20
+ def opencode_go_provider() -> Provider:
21
+ """OpenCode Go (mixed APIs), authenticated with ``OPENCODE_API_KEY``."""
22
+ return create_provider(
23
+ id="opencode-go",
24
+ name="OpenCode Go",
25
+ auth=ProviderAuth(api_key=env_api_key_auth("OpenCode API key", ["OPENCODE_API_KEY"])),
26
+ models=lambda: catalog_models("opencode-go"),
27
+ api={
28
+ "anthropic-messages": with_opencode_session_header(anthropic_messages_api()),
29
+ "google-generative-ai": with_opencode_session_header(google_generative_ai_api()),
30
+ "openai-completions": with_opencode_session_header(openai_completions_api()),
31
+ "openai-responses": with_opencode_session_header(openai_responses_api()),
32
+ },
33
+ )
@@ -0,0 +1,60 @@
1
+ """OpenCode's per-conversation routing header."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from typing import Any
7
+
8
+ from ..event_stream import AssistantMessageEventStream
9
+ from ..types import Model, TranscriptContext
10
+
11
+ __all__ = ["OPENCODE_SESSION_HEADER", "with_opencode_session_header"]
12
+
13
+ OPENCODE_SESSION_HEADER = "x-opencode-session"
14
+
15
+
16
+ def _has_header(headers: Mapping[str, Any] | None, name: str) -> bool:
17
+ # Any caller entry counts, including a ``None`` suppression marker.
18
+ expected = name.lower()
19
+ return any(key.lower() == expected for key in headers or {})
20
+
21
+
22
+ def _with_session_header(options: Mapping[str, Any] | None) -> Mapping[str, Any] | None:
23
+ if not options or not options.get("session_id") or _has_header(options.get("headers"), OPENCODE_SESSION_HEADER):
24
+ return options
25
+ return {
26
+ **options,
27
+ "headers": {**(options.get("headers") or {}), OPENCODE_SESSION_HEADER: options["session_id"]},
28
+ }
29
+
30
+
31
+ class _OpenCodeStreams:
32
+ """Delegating ``ProviderStreams`` that keeps the wrapped deferred capabilities."""
33
+
34
+ __slots__ = ("_streams", "cancel_deferred", "fetch_deferred")
35
+
36
+ def __init__(self, streams: Any) -> None:
37
+ self._streams = streams
38
+ self.fetch_deferred = getattr(streams, "fetch_deferred", None)
39
+ self.cancel_deferred = getattr(streams, "cancel_deferred", None)
40
+
41
+ def stream(
42
+ self, model: Model, context: TranscriptContext, options: Mapping[str, Any] | None = None
43
+ ) -> AssistantMessageEventStream:
44
+ return self._streams.stream(model, context, _with_session_header(options))
45
+
46
+ def stream_simple(
47
+ self, model: Model, context: TranscriptContext, options: Mapping[str, Any] | None = None
48
+ ) -> AssistantMessageEventStream:
49
+ return self._streams.stream_simple(model, context, _with_session_header(options))
50
+
51
+ def __repr__(self) -> str:
52
+ return f"with_opencode_session_header({self._streams!r})"
53
+
54
+
55
+ def with_opencode_session_header(streams: Any) -> Any:
56
+ """Add OpenCode's required ``x-opencode-session`` header from ``session_id`` before API dispatch.
57
+
58
+ A caller-supplied header with the same name (any case, including ``None``) wins.
59
+ """
60
+ return _OpenCodeStreams(streams)