mirascope 2.0.0a3__py3-none-any.whl → 2.0.0a5__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.
- mirascope/api/_generated/__init__.py +78 -6
- mirascope/api/_generated/api_keys/__init__.py +7 -0
- mirascope/api/_generated/api_keys/client.py +453 -0
- mirascope/api/_generated/api_keys/raw_client.py +853 -0
- mirascope/api/_generated/api_keys/types/__init__.py +9 -0
- mirascope/api/_generated/api_keys/types/api_keys_create_response.py +36 -0
- mirascope/api/_generated/api_keys/types/api_keys_get_response.py +35 -0
- mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +35 -0
- mirascope/api/_generated/client.py +14 -0
- mirascope/api/_generated/environments/__init__.py +17 -0
- mirascope/api/_generated/environments/client.py +532 -0
- mirascope/api/_generated/environments/raw_client.py +1088 -0
- mirascope/api/_generated/environments/types/__init__.py +15 -0
- mirascope/api/_generated/environments/types/environments_create_response.py +26 -0
- mirascope/api/_generated/environments/types/environments_get_response.py +26 -0
- mirascope/api/_generated/environments/types/environments_list_response_item.py +26 -0
- mirascope/api/_generated/environments/types/environments_update_response.py +26 -0
- mirascope/api/_generated/errors/__init__.py +11 -1
- mirascope/api/_generated/errors/conflict_error.py +15 -0
- mirascope/api/_generated/errors/forbidden_error.py +15 -0
- mirascope/api/_generated/errors/internal_server_error.py +15 -0
- mirascope/api/_generated/errors/not_found_error.py +15 -0
- mirascope/api/_generated/organizations/__init__.py +25 -0
- mirascope/api/_generated/organizations/client.py +404 -0
- mirascope/api/_generated/organizations/raw_client.py +902 -0
- mirascope/api/_generated/organizations/types/__init__.py +23 -0
- mirascope/api/_generated/organizations/types/organizations_create_response.py +25 -0
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_get_response.py +25 -0
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +25 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_response.py +25 -0
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +7 -0
- mirascope/api/_generated/projects/__init__.py +17 -0
- mirascope/api/_generated/projects/client.py +482 -0
- mirascope/api/_generated/projects/raw_client.py +1058 -0
- mirascope/api/_generated/projects/types/__init__.py +15 -0
- mirascope/api/_generated/projects/types/projects_create_response.py +31 -0
- mirascope/api/_generated/projects/types/projects_get_response.py +31 -0
- mirascope/api/_generated/projects/types/projects_list_response_item.py +31 -0
- mirascope/api/_generated/projects/types/projects_update_response.py +31 -0
- mirascope/api/_generated/reference.md +1311 -0
- mirascope/api/_generated/types/__init__.py +20 -4
- mirascope/api/_generated/types/already_exists_error.py +24 -0
- mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
- mirascope/api/_generated/types/database_error.py +24 -0
- mirascope/api/_generated/types/database_error_tag.py +5 -0
- mirascope/api/_generated/types/http_api_decode_error.py +1 -3
- mirascope/api/_generated/types/issue.py +1 -5
- mirascope/api/_generated/types/not_found_error_body.py +24 -0
- mirascope/api/_generated/types/not_found_error_tag.py +5 -0
- mirascope/api/_generated/types/permission_denied_error.py +24 -0
- mirascope/api/_generated/types/permission_denied_error_tag.py +7 -0
- mirascope/api/_generated/types/property_key.py +2 -2
- mirascope/api/_generated/types/{property_key_tag.py → property_key_key.py} +3 -5
- mirascope/api/_generated/types/{property_key_tag_tag.py → property_key_key_tag.py} +1 -1
- mirascope/llm/__init__.py +6 -2
- mirascope/llm/exceptions.py +28 -0
- mirascope/llm/providers/__init__.py +12 -4
- mirascope/llm/providers/anthropic/__init__.py +6 -1
- mirascope/llm/providers/anthropic/_utils/__init__.py +17 -5
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +271 -0
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +216 -0
- mirascope/llm/providers/anthropic/_utils/decode.py +39 -7
- mirascope/llm/providers/anthropic/_utils/encode.py +156 -64
- mirascope/llm/providers/anthropic/_utils/errors.py +46 -0
- mirascope/llm/providers/anthropic/beta_provider.py +328 -0
- mirascope/llm/providers/anthropic/model_id.py +10 -27
- mirascope/llm/providers/anthropic/model_info.py +87 -0
- mirascope/llm/providers/anthropic/provider.py +132 -145
- mirascope/llm/providers/base/__init__.py +2 -1
- mirascope/llm/providers/base/_utils.py +15 -1
- mirascope/llm/providers/base/base_provider.py +173 -58
- mirascope/llm/providers/google/_utils/__init__.py +2 -0
- mirascope/llm/providers/google/_utils/decode.py +55 -3
- mirascope/llm/providers/google/_utils/encode.py +14 -6
- mirascope/llm/providers/google/_utils/errors.py +49 -0
- mirascope/llm/providers/google/model_id.py +7 -13
- mirascope/llm/providers/google/model_info.py +62 -0
- mirascope/llm/providers/google/provider.py +13 -8
- mirascope/llm/providers/mlx/_utils.py +31 -2
- mirascope/llm/providers/mlx/encoding/transformers.py +17 -1
- mirascope/llm/providers/mlx/provider.py +12 -0
- mirascope/llm/providers/ollama/__init__.py +19 -0
- mirascope/llm/providers/ollama/provider.py +71 -0
- mirascope/llm/providers/openai/__init__.py +10 -1
- mirascope/llm/providers/openai/_utils/__init__.py +5 -0
- mirascope/llm/providers/openai/_utils/errors.py +46 -0
- mirascope/llm/providers/openai/completions/__init__.py +6 -1
- mirascope/llm/providers/openai/completions/_utils/decode.py +57 -5
- mirascope/llm/providers/openai/completions/_utils/encode.py +9 -8
- mirascope/llm/providers/openai/completions/base_provider.py +513 -0
- mirascope/llm/providers/openai/completions/provider.py +13 -447
- mirascope/llm/providers/openai/model_info.py +57 -0
- mirascope/llm/providers/openai/provider.py +30 -5
- mirascope/llm/providers/openai/responses/_utils/decode.py +55 -4
- mirascope/llm/providers/openai/responses/_utils/encode.py +9 -9
- mirascope/llm/providers/openai/responses/provider.py +33 -28
- mirascope/llm/providers/provider_id.py +11 -1
- mirascope/llm/providers/provider_registry.py +59 -4
- mirascope/llm/providers/together/__init__.py +19 -0
- mirascope/llm/providers/together/provider.py +40 -0
- mirascope/llm/responses/__init__.py +3 -0
- mirascope/llm/responses/base_response.py +4 -0
- mirascope/llm/responses/base_stream_response.py +25 -1
- mirascope/llm/responses/finish_reason.py +1 -0
- mirascope/llm/responses/response.py +9 -0
- mirascope/llm/responses/root_response.py +5 -1
- mirascope/llm/responses/usage.py +95 -0
- mirascope/ops/_internal/closure.py +62 -11
- {mirascope-2.0.0a3.dist-info → mirascope-2.0.0a5.dist-info}/METADATA +3 -3
- {mirascope-2.0.0a3.dist-info → mirascope-2.0.0a5.dist-info}/RECORD +115 -56
- mirascope/llm/providers/load_provider.py +0 -48
- mirascope/llm/providers/openai/shared/__init__.py +0 -7
- mirascope/llm/providers/openai/shared/_utils.py +0 -59
- {mirascope-2.0.0a3.dist-info → mirascope-2.0.0a5.dist-info}/WHEEL +0 -0
- {mirascope-2.0.0a3.dist-info → mirascope-2.0.0a5.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,456 +1,22 @@
|
|
|
1
|
-
"""OpenAI
|
|
1
|
+
"""OpenAI Completions API provider implementation."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
from typing_extensions import Unpack
|
|
3
|
+
from ..model_id import model_name
|
|
4
|
+
from .base_provider import BaseOpenAICompletionsProvider
|
|
6
5
|
|
|
7
|
-
from openai import AsyncOpenAI, OpenAI
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from ....messages import Message
|
|
12
|
-
from ....responses import (
|
|
13
|
-
AsyncContextResponse,
|
|
14
|
-
AsyncContextStreamResponse,
|
|
15
|
-
AsyncResponse,
|
|
16
|
-
AsyncStreamResponse,
|
|
17
|
-
ContextResponse,
|
|
18
|
-
ContextStreamResponse,
|
|
19
|
-
Response,
|
|
20
|
-
StreamResponse,
|
|
21
|
-
)
|
|
22
|
-
from ....tools import (
|
|
23
|
-
AsyncContextTool,
|
|
24
|
-
AsyncContextToolkit,
|
|
25
|
-
AsyncTool,
|
|
26
|
-
AsyncToolkit,
|
|
27
|
-
ContextTool,
|
|
28
|
-
ContextToolkit,
|
|
29
|
-
Tool,
|
|
30
|
-
Toolkit,
|
|
31
|
-
)
|
|
32
|
-
from ...base import BaseProvider, Params
|
|
33
|
-
from ..model_id import OpenAIModelId, model_name
|
|
34
|
-
from . import _utils
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class OpenAICompletionsProvider(BaseProvider[OpenAI]):
|
|
38
|
-
"""The client for the OpenAI LLM model."""
|
|
7
|
+
class OpenAICompletionsProvider(BaseOpenAICompletionsProvider):
|
|
8
|
+
"""Provider for OpenAI's ChatCompletions API."""
|
|
39
9
|
|
|
40
10
|
id = "openai:completions"
|
|
41
11
|
default_scope = "openai/"
|
|
12
|
+
default_base_url = None
|
|
13
|
+
api_key_env_var = "OPENAI_API_KEY"
|
|
14
|
+
api_key_required = False
|
|
15
|
+
provider_name = "OpenAI"
|
|
42
16
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
*,
|
|
46
|
-
api_key: str | None = None,
|
|
47
|
-
base_url: str | None = None,
|
|
48
|
-
wrapped_by_openai_provider: bool = False,
|
|
49
|
-
) -> None:
|
|
50
|
-
"""Initialize the OpenAI client."""
|
|
51
|
-
self.client = OpenAI(api_key=api_key, base_url=base_url)
|
|
52
|
-
self.async_client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
|
53
|
-
self.active_provider_id: Literal["openai", "openai:completions"] = (
|
|
54
|
-
"openai" if wrapped_by_openai_provider else "openai:completions"
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
def _call(
|
|
58
|
-
self,
|
|
59
|
-
*,
|
|
60
|
-
model_id: OpenAIModelId,
|
|
61
|
-
messages: Sequence[Message],
|
|
62
|
-
tools: Sequence[Tool] | Toolkit | None = None,
|
|
63
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
64
|
-
**params: Unpack[Params],
|
|
65
|
-
) -> Response | Response[FormattableT]:
|
|
66
|
-
"""Generate an `llm.Response` by synchronously calling the OpenAI ChatCompletions API.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
model_id: Model identifier to use.
|
|
70
|
-
messages: Messages to send to the LLM.
|
|
71
|
-
tools: Optional tools that the model may invoke.
|
|
72
|
-
format: Optional response format specifier.
|
|
73
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
74
|
-
|
|
75
|
-
Returns:
|
|
76
|
-
An `llm.Response` object containing the LLM-generated content.
|
|
77
|
-
"""
|
|
78
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
79
|
-
model_id=model_id,
|
|
80
|
-
messages=messages,
|
|
81
|
-
tools=tools,
|
|
82
|
-
format=format,
|
|
83
|
-
params=params,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
openai_response = self.client.chat.completions.create(**kwargs)
|
|
87
|
-
|
|
88
|
-
assistant_message, finish_reason = _utils.decode_response(
|
|
89
|
-
openai_response, model_id, self.active_provider_id
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return Response(
|
|
93
|
-
raw=openai_response,
|
|
94
|
-
provider_id=self.active_provider_id,
|
|
95
|
-
model_id=model_id,
|
|
96
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
97
|
-
params=params,
|
|
98
|
-
tools=tools,
|
|
99
|
-
input_messages=input_messages,
|
|
100
|
-
assistant_message=assistant_message,
|
|
101
|
-
finish_reason=finish_reason,
|
|
102
|
-
format=format,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
def _context_call(
|
|
106
|
-
self,
|
|
107
|
-
*,
|
|
108
|
-
ctx: Context[DepsT],
|
|
109
|
-
model_id: OpenAIModelId,
|
|
110
|
-
messages: Sequence[Message],
|
|
111
|
-
tools: Sequence[Tool | ContextTool[DepsT]]
|
|
112
|
-
| ContextToolkit[DepsT]
|
|
113
|
-
| None = None,
|
|
114
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
115
|
-
**params: Unpack[Params],
|
|
116
|
-
) -> ContextResponse[DepsT, None] | ContextResponse[DepsT, FormattableT]:
|
|
117
|
-
"""Generate an `llm.ContextResponse` by synchronously calling the OpenAI ChatCompletions API.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
ctx: Context object with dependencies for tools.
|
|
121
|
-
model_id: Model identifier to use.
|
|
122
|
-
messages: Messages to send to the LLM.
|
|
123
|
-
tools: Optional tools that the model may invoke.
|
|
124
|
-
format: Optional response format specifier.
|
|
125
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
An `llm.ContextResponse` object containing the LLM-generated content.
|
|
129
|
-
"""
|
|
130
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
131
|
-
model_id=model_id,
|
|
132
|
-
messages=messages,
|
|
133
|
-
tools=tools,
|
|
134
|
-
format=format,
|
|
135
|
-
params=params,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
openai_response = self.client.chat.completions.create(**kwargs)
|
|
139
|
-
|
|
140
|
-
assistant_message, finish_reason = _utils.decode_response(
|
|
141
|
-
openai_response, model_id, self.active_provider_id
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
return ContextResponse(
|
|
145
|
-
raw=openai_response,
|
|
146
|
-
provider_id=self.active_provider_id,
|
|
147
|
-
model_id=model_id,
|
|
148
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
149
|
-
params=params,
|
|
150
|
-
tools=tools,
|
|
151
|
-
input_messages=input_messages,
|
|
152
|
-
assistant_message=assistant_message,
|
|
153
|
-
finish_reason=finish_reason,
|
|
154
|
-
format=format,
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
async def _call_async(
|
|
158
|
-
self,
|
|
159
|
-
*,
|
|
160
|
-
model_id: OpenAIModelId,
|
|
161
|
-
messages: Sequence[Message],
|
|
162
|
-
tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
|
|
163
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
164
|
-
**params: Unpack[Params],
|
|
165
|
-
) -> AsyncResponse | AsyncResponse[FormattableT]:
|
|
166
|
-
"""Generate an `llm.AsyncResponse` by asynchronously calling the OpenAI ChatCompletions API.
|
|
167
|
-
|
|
168
|
-
Args:
|
|
169
|
-
model_id: Model identifier to use.
|
|
170
|
-
messages: Messages to send to the LLM.
|
|
171
|
-
tools: Optional tools that the model may invoke.
|
|
172
|
-
format: Optional response format specifier.
|
|
173
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
174
|
-
|
|
175
|
-
Returns:
|
|
176
|
-
An `llm.AsyncResponse` object containing the LLM-generated content.
|
|
177
|
-
"""
|
|
178
|
-
|
|
179
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
180
|
-
model_id=model_id,
|
|
181
|
-
params=params,
|
|
182
|
-
messages=messages,
|
|
183
|
-
tools=tools,
|
|
184
|
-
format=format,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
openai_response = await self.async_client.chat.completions.create(**kwargs)
|
|
188
|
-
|
|
189
|
-
assistant_message, finish_reason = _utils.decode_response(
|
|
190
|
-
openai_response, model_id, self.active_provider_id
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
return AsyncResponse(
|
|
194
|
-
raw=openai_response,
|
|
195
|
-
provider_id=self.active_provider_id,
|
|
196
|
-
model_id=model_id,
|
|
197
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
198
|
-
params=params,
|
|
199
|
-
tools=tools,
|
|
200
|
-
input_messages=input_messages,
|
|
201
|
-
assistant_message=assistant_message,
|
|
202
|
-
finish_reason=finish_reason,
|
|
203
|
-
format=format,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
async def _context_call_async(
|
|
207
|
-
self,
|
|
208
|
-
*,
|
|
209
|
-
ctx: Context[DepsT],
|
|
210
|
-
model_id: OpenAIModelId,
|
|
211
|
-
messages: Sequence[Message],
|
|
212
|
-
tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
|
|
213
|
-
| AsyncContextToolkit[DepsT]
|
|
214
|
-
| None = None,
|
|
215
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
216
|
-
**params: Unpack[Params],
|
|
217
|
-
) -> AsyncContextResponse[DepsT, None] | AsyncContextResponse[DepsT, FormattableT]:
|
|
218
|
-
"""Generate an `llm.AsyncContextResponse` by asynchronously calling the OpenAI ChatCompletions API.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
ctx: Context object with dependencies for tools.
|
|
222
|
-
model_id: Model identifier to use.
|
|
223
|
-
messages: Messages to send to the LLM.
|
|
224
|
-
tools: Optional tools that the model may invoke.
|
|
225
|
-
format: Optional response format specifier.
|
|
226
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
An `llm.AsyncContextResponse` object containing the LLM-generated content.
|
|
230
|
-
"""
|
|
231
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
232
|
-
model_id=model_id,
|
|
233
|
-
params=params,
|
|
234
|
-
messages=messages,
|
|
235
|
-
tools=tools,
|
|
236
|
-
format=format,
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
openai_response = await self.async_client.chat.completions.create(**kwargs)
|
|
17
|
+
def _provider_model_name(self, model_id: str) -> str:
|
|
18
|
+
"""Get the model name for tracking in Response.
|
|
240
19
|
|
|
241
|
-
|
|
242
|
-
openai_response, model_id, self.active_provider_id
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
return AsyncContextResponse(
|
|
246
|
-
raw=openai_response,
|
|
247
|
-
provider_id=self.active_provider_id,
|
|
248
|
-
model_id=model_id,
|
|
249
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
250
|
-
params=params,
|
|
251
|
-
tools=tools,
|
|
252
|
-
input_messages=input_messages,
|
|
253
|
-
assistant_message=assistant_message,
|
|
254
|
-
finish_reason=finish_reason,
|
|
255
|
-
format=format,
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
def _stream(
|
|
259
|
-
self,
|
|
260
|
-
*,
|
|
261
|
-
model_id: OpenAIModelId,
|
|
262
|
-
messages: Sequence[Message],
|
|
263
|
-
tools: Sequence[Tool] | Toolkit | None = None,
|
|
264
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
265
|
-
**params: Unpack[Params],
|
|
266
|
-
) -> StreamResponse | StreamResponse[FormattableT]:
|
|
267
|
-
"""Generate an `llm.StreamResponse` by synchronously streaming from the OpenAI ChatCompletions API.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
model_id: Model identifier to use.
|
|
271
|
-
messages: Messages to send to the LLM.
|
|
272
|
-
tools: Optional tools that the model may invoke.
|
|
273
|
-
format: Optional response format specifier.
|
|
274
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
An `llm.StreamResponse` object for iterating over the LLM-generated content.
|
|
20
|
+
Returns the model name with :completions suffix for tracking which API was used.
|
|
278
21
|
"""
|
|
279
|
-
|
|
280
|
-
model_id=model_id,
|
|
281
|
-
messages=messages,
|
|
282
|
-
tools=tools,
|
|
283
|
-
format=format,
|
|
284
|
-
params=params,
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
openai_stream = self.client.chat.completions.create(
|
|
288
|
-
**kwargs,
|
|
289
|
-
stream=True,
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
chunk_iterator = _utils.decode_stream(openai_stream)
|
|
293
|
-
|
|
294
|
-
return StreamResponse(
|
|
295
|
-
provider_id=self.active_provider_id,
|
|
296
|
-
model_id=model_id,
|
|
297
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
298
|
-
params=params,
|
|
299
|
-
tools=tools,
|
|
300
|
-
input_messages=input_messages,
|
|
301
|
-
chunk_iterator=chunk_iterator,
|
|
302
|
-
format=format,
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
def _context_stream(
|
|
306
|
-
self,
|
|
307
|
-
*,
|
|
308
|
-
ctx: Context[DepsT],
|
|
309
|
-
model_id: OpenAIModelId,
|
|
310
|
-
messages: Sequence[Message],
|
|
311
|
-
tools: Sequence[Tool | ContextTool[DepsT]]
|
|
312
|
-
| ContextToolkit[DepsT]
|
|
313
|
-
| None = None,
|
|
314
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
315
|
-
**params: Unpack[Params],
|
|
316
|
-
) -> ContextStreamResponse[DepsT] | ContextStreamResponse[DepsT, FormattableT]:
|
|
317
|
-
"""Generate an `llm.ContextStreamResponse` by synchronously streaming from the OpenAI ChatCompletions API.
|
|
318
|
-
|
|
319
|
-
Args:
|
|
320
|
-
ctx: Context object with dependencies for tools.
|
|
321
|
-
model_id: Model identifier to use.
|
|
322
|
-
messages: Messages to send to the LLM.
|
|
323
|
-
tools: Optional tools that the model may invoke.
|
|
324
|
-
format: Optional response format specifier.
|
|
325
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
326
|
-
|
|
327
|
-
Returns:
|
|
328
|
-
An `llm.ContextStreamResponse` object for iterating over the LLM-generated content.
|
|
329
|
-
"""
|
|
330
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
331
|
-
model_id=model_id,
|
|
332
|
-
messages=messages,
|
|
333
|
-
tools=tools,
|
|
334
|
-
format=format,
|
|
335
|
-
params=params,
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
openai_stream = self.client.chat.completions.create(
|
|
339
|
-
**kwargs,
|
|
340
|
-
stream=True,
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
chunk_iterator = _utils.decode_stream(openai_stream)
|
|
344
|
-
|
|
345
|
-
return ContextStreamResponse(
|
|
346
|
-
provider_id=self.active_provider_id,
|
|
347
|
-
model_id=model_id,
|
|
348
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
349
|
-
params=params,
|
|
350
|
-
tools=tools,
|
|
351
|
-
input_messages=input_messages,
|
|
352
|
-
chunk_iterator=chunk_iterator,
|
|
353
|
-
format=format,
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
async def _stream_async(
|
|
357
|
-
self,
|
|
358
|
-
*,
|
|
359
|
-
model_id: OpenAIModelId,
|
|
360
|
-
messages: Sequence[Message],
|
|
361
|
-
tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
|
|
362
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
363
|
-
**params: Unpack[Params],
|
|
364
|
-
) -> AsyncStreamResponse | AsyncStreamResponse[FormattableT]:
|
|
365
|
-
"""Generate an `llm.AsyncStreamResponse` by asynchronously streaming from the OpenAI ChatCompletions API.
|
|
366
|
-
|
|
367
|
-
Args:
|
|
368
|
-
model_id: Model identifier to use.
|
|
369
|
-
messages: Messages to send to the LLM.
|
|
370
|
-
tools: Optional tools that the model may invoke.
|
|
371
|
-
format: Optional response format specifier.
|
|
372
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
373
|
-
|
|
374
|
-
Returns:
|
|
375
|
-
An `llm.AsyncStreamResponse` object for asynchronously iterating over the LLM-generated content.
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
379
|
-
model_id=model_id,
|
|
380
|
-
messages=messages,
|
|
381
|
-
tools=tools,
|
|
382
|
-
format=format,
|
|
383
|
-
params=params,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
openai_stream = await self.async_client.chat.completions.create(
|
|
387
|
-
**kwargs,
|
|
388
|
-
stream=True,
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
chunk_iterator = _utils.decode_async_stream(openai_stream)
|
|
392
|
-
|
|
393
|
-
return AsyncStreamResponse(
|
|
394
|
-
provider_id=self.active_provider_id,
|
|
395
|
-
model_id=model_id,
|
|
396
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
397
|
-
params=params,
|
|
398
|
-
tools=tools,
|
|
399
|
-
input_messages=input_messages,
|
|
400
|
-
chunk_iterator=chunk_iterator,
|
|
401
|
-
format=format,
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
async def _context_stream_async(
|
|
405
|
-
self,
|
|
406
|
-
*,
|
|
407
|
-
ctx: Context[DepsT],
|
|
408
|
-
model_id: OpenAIModelId,
|
|
409
|
-
messages: Sequence[Message],
|
|
410
|
-
tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
|
|
411
|
-
| AsyncContextToolkit[DepsT]
|
|
412
|
-
| None = None,
|
|
413
|
-
format: type[FormattableT] | Format[FormattableT] | None = None,
|
|
414
|
-
**params: Unpack[Params],
|
|
415
|
-
) -> (
|
|
416
|
-
AsyncContextStreamResponse[DepsT]
|
|
417
|
-
| AsyncContextStreamResponse[DepsT, FormattableT]
|
|
418
|
-
):
|
|
419
|
-
"""Generate an `llm.AsyncContextStreamResponse` by asynchronously streaming from the OpenAI ChatCompletions API.
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
ctx: Context object with dependencies for tools.
|
|
423
|
-
model_id: Model identifier to use.
|
|
424
|
-
messages: Messages to send to the LLM.
|
|
425
|
-
tools: Optional tools that the model may invoke.
|
|
426
|
-
format: Optional response format specifier.
|
|
427
|
-
**params: Additional parameters to configure output (e.g. temperature). See `llm.Params`.
|
|
428
|
-
|
|
429
|
-
Returns:
|
|
430
|
-
An `llm.AsyncContextStreamResponse` object for asynchronously iterating over the LLM-generated content.
|
|
431
|
-
"""
|
|
432
|
-
input_messages, format, kwargs = _utils.encode_request(
|
|
433
|
-
model_id=model_id,
|
|
434
|
-
messages=messages,
|
|
435
|
-
tools=tools,
|
|
436
|
-
format=format,
|
|
437
|
-
params=params,
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
openai_stream = await self.async_client.chat.completions.create(
|
|
441
|
-
**kwargs,
|
|
442
|
-
stream=True,
|
|
443
|
-
)
|
|
444
|
-
|
|
445
|
-
chunk_iterator = _utils.decode_async_stream(openai_stream)
|
|
446
|
-
|
|
447
|
-
return AsyncContextStreamResponse(
|
|
448
|
-
provider_id=self.active_provider_id,
|
|
449
|
-
model_id=model_id,
|
|
450
|
-
provider_model_name=model_name(model_id, "completions"),
|
|
451
|
-
params=params,
|
|
452
|
-
tools=tools,
|
|
453
|
-
input_messages=input_messages,
|
|
454
|
-
chunk_iterator=chunk_iterator,
|
|
455
|
-
format=format,
|
|
456
|
-
)
|
|
22
|
+
return model_name(model_id, "completions")
|
|
@@ -131,6 +131,19 @@ OpenAIKnownModels = Literal[
|
|
|
131
131
|
"openai/gpt-5.1-codex-max:responses",
|
|
132
132
|
"openai/gpt-5.1-codex-mini",
|
|
133
133
|
"openai/gpt-5.1-codex-mini:responses",
|
|
134
|
+
"openai/gpt-5.2",
|
|
135
|
+
"openai/gpt-5.2:completions",
|
|
136
|
+
"openai/gpt-5.2:responses",
|
|
137
|
+
"openai/gpt-5.2-2025-12-11",
|
|
138
|
+
"openai/gpt-5.2-2025-12-11:completions",
|
|
139
|
+
"openai/gpt-5.2-2025-12-11:responses",
|
|
140
|
+
"openai/gpt-5.2-chat-latest",
|
|
141
|
+
"openai/gpt-5.2-chat-latest:completions",
|
|
142
|
+
"openai/gpt-5.2-chat-latest:responses",
|
|
143
|
+
"openai/gpt-5.2-pro",
|
|
144
|
+
"openai/gpt-5.2-pro:responses",
|
|
145
|
+
"openai/gpt-5.2-pro-2025-12-11",
|
|
146
|
+
"openai/gpt-5.2-pro-2025-12-11:responses",
|
|
134
147
|
"openai/o1",
|
|
135
148
|
"openai/o1:completions",
|
|
136
149
|
"openai/o1:responses",
|
|
@@ -168,9 +181,11 @@ OpenAIKnownModels = Literal[
|
|
|
168
181
|
|
|
169
182
|
|
|
170
183
|
MODELS_WITHOUT_AUDIO_SUPPORT: set[str] = {
|
|
184
|
+
"chatgpt-4o-latest",
|
|
171
185
|
"gpt-3.5-turbo",
|
|
172
186
|
"gpt-3.5-turbo-0125",
|
|
173
187
|
"gpt-3.5-turbo-1106",
|
|
188
|
+
"gpt-3.5-turbo-16k",
|
|
174
189
|
"gpt-4",
|
|
175
190
|
"gpt-4-0125-preview",
|
|
176
191
|
"gpt-4-0613",
|
|
@@ -179,15 +194,19 @@ MODELS_WITHOUT_AUDIO_SUPPORT: set[str] = {
|
|
|
179
194
|
"gpt-4-turbo-2024-04-09",
|
|
180
195
|
"gpt-4-turbo-preview",
|
|
181
196
|
"gpt-4.1",
|
|
197
|
+
"gpt-4.1-2025-04-14",
|
|
182
198
|
"gpt-4.1-mini",
|
|
199
|
+
"gpt-4.1-mini-2025-04-14",
|
|
183
200
|
"gpt-4.1-nano",
|
|
184
201
|
"gpt-4.1-nano-2025-04-14",
|
|
185
202
|
"gpt-4o",
|
|
186
203
|
"gpt-4o-2024-05-13",
|
|
187
204
|
"gpt-4o-2024-08-06",
|
|
205
|
+
"gpt-4o-2024-11-20",
|
|
188
206
|
"gpt-4o-mini",
|
|
189
207
|
"gpt-4o-mini-2024-07-18",
|
|
190
208
|
"gpt-4o-mini-search-preview",
|
|
209
|
+
"gpt-4o-mini-search-preview-2025-03-11",
|
|
191
210
|
"gpt-4o-search-preview",
|
|
192
211
|
"gpt-4o-search-preview-2025-03-11",
|
|
193
212
|
"gpt-5",
|
|
@@ -200,6 +219,9 @@ MODELS_WITHOUT_AUDIO_SUPPORT: set[str] = {
|
|
|
200
219
|
"gpt-5-search-api",
|
|
201
220
|
"gpt-5-search-api-2025-10-14",
|
|
202
221
|
"gpt-5.1-chat-latest",
|
|
222
|
+
"gpt-5.2",
|
|
223
|
+
"gpt-5.2-2025-12-11",
|
|
224
|
+
"gpt-5.2-chat-latest",
|
|
203
225
|
"o1",
|
|
204
226
|
"o1-2024-12-17",
|
|
205
227
|
"o3",
|
|
@@ -244,3 +266,38 @@ NON_REASONING_MODELS: set[str] = {
|
|
|
244
266
|
|
|
245
267
|
Models not in this set are assumed to support reasoning (optimistic default).
|
|
246
268
|
"""
|
|
269
|
+
|
|
270
|
+
MODELS_WITHOUT_JSON_SCHEMA_SUPPORT: set[str] = {
|
|
271
|
+
"chatgpt-4o-latest",
|
|
272
|
+
"gpt-3.5-turbo",
|
|
273
|
+
"gpt-3.5-turbo-0125",
|
|
274
|
+
"gpt-3.5-turbo-1106",
|
|
275
|
+
"gpt-3.5-turbo-16k",
|
|
276
|
+
"gpt-4",
|
|
277
|
+
"gpt-4-0125-preview",
|
|
278
|
+
"gpt-4-0613",
|
|
279
|
+
"gpt-4-1106-preview",
|
|
280
|
+
"gpt-4-turbo",
|
|
281
|
+
"gpt-4-turbo-2024-04-09",
|
|
282
|
+
"gpt-4-turbo-preview",
|
|
283
|
+
"gpt-4o-2024-05-13",
|
|
284
|
+
}
|
|
285
|
+
"""Models that do not support JSON schema (structured outputs).
|
|
286
|
+
|
|
287
|
+
Models not in this set are assumed to support JSON schema (optimistic default).
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
MODELS_WITHOUT_JSON_OBJECT_SUPPORT: set[str] = {
|
|
291
|
+
"gpt-4",
|
|
292
|
+
"gpt-4-0613",
|
|
293
|
+
"gpt-4o-mini-search-preview",
|
|
294
|
+
"gpt-4o-mini-search-preview-2025-03-11",
|
|
295
|
+
"gpt-4o-search-preview",
|
|
296
|
+
"gpt-4o-search-preview-2025-03-11",
|
|
297
|
+
"gpt-5-search-api",
|
|
298
|
+
"gpt-5-search-api-2025-10-14",
|
|
299
|
+
}
|
|
300
|
+
"""Models that do not support JSON object mode.
|
|
301
|
+
|
|
302
|
+
Models not in this set are assumed to support JSON object mode (optimistic default).
|
|
303
|
+
"""
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
4
|
from typing_extensions import Unpack
|
|
5
5
|
|
|
6
|
-
from openai import OpenAI
|
|
6
|
+
from openai import BadRequestError as OpenAIBadRequestError, OpenAI
|
|
7
7
|
|
|
8
8
|
from ...context import Context, DepsT
|
|
9
|
+
from ...exceptions import BadRequestError, NotFoundError
|
|
9
10
|
from ...formatting import Format, FormattableT
|
|
10
11
|
from ...messages import Message
|
|
11
12
|
from ...responses import (
|
|
@@ -29,6 +30,7 @@ from ...tools import (
|
|
|
29
30
|
Toolkit,
|
|
30
31
|
)
|
|
31
32
|
from ..base import BaseProvider, Params
|
|
33
|
+
from . import _utils
|
|
32
34
|
from .completions import OpenAICompletionsProvider
|
|
33
35
|
from .model_id import OPENAI_KNOWN_MODELS, OpenAIModelId
|
|
34
36
|
from .responses import OpenAIResponsesProvider
|
|
@@ -90,25 +92,48 @@ def choose_api_mode(model_id: OpenAIModelId, messages: Sequence[Message]) -> str
|
|
|
90
92
|
return "completions"
|
|
91
93
|
|
|
92
94
|
|
|
95
|
+
class OpenAIRoutedCompletionsProvider(OpenAICompletionsProvider):
|
|
96
|
+
"""OpenAI completions client that reports provider_id as 'openai'."""
|
|
97
|
+
|
|
98
|
+
id = "openai"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class OpenAIRoutedResponsesProvider(OpenAIResponsesProvider):
|
|
102
|
+
"""OpenAI responses client that reports provider_id as 'openai'."""
|
|
103
|
+
|
|
104
|
+
id = "openai"
|
|
105
|
+
|
|
106
|
+
|
|
93
107
|
class OpenAIProvider(BaseProvider[OpenAI]):
|
|
94
108
|
"""Unified provider for OpenAI that routes to Completions or Responses API based on model_id."""
|
|
95
109
|
|
|
96
110
|
id = "openai"
|
|
97
111
|
default_scope = "openai/"
|
|
112
|
+
# Include special handling for model_not_found from Responses API
|
|
113
|
+
error_map = {
|
|
114
|
+
**_utils.OPENAI_ERROR_MAP,
|
|
115
|
+
OpenAIBadRequestError: lambda e: NotFoundError
|
|
116
|
+
if hasattr(e, "code") and e.code == "model_not_found" # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
117
|
+
else BadRequestError,
|
|
118
|
+
}
|
|
98
119
|
|
|
99
120
|
def __init__(
|
|
100
121
|
self, *, api_key: str | None = None, base_url: str | None = None
|
|
101
122
|
) -> None:
|
|
102
123
|
"""Initialize the OpenAI provider with both subclients."""
|
|
103
|
-
self._completions_provider =
|
|
104
|
-
api_key=api_key, base_url=base_url
|
|
124
|
+
self._completions_provider = OpenAIRoutedCompletionsProvider(
|
|
125
|
+
api_key=api_key, base_url=base_url
|
|
105
126
|
)
|
|
106
|
-
self._responses_provider =
|
|
107
|
-
api_key=api_key, base_url=base_url
|
|
127
|
+
self._responses_provider = OpenAIRoutedResponsesProvider(
|
|
128
|
+
api_key=api_key, base_url=base_url
|
|
108
129
|
)
|
|
109
130
|
# Use completions client's underlying OpenAI client as the main one
|
|
110
131
|
self.client = self._completions_provider.client
|
|
111
132
|
|
|
133
|
+
def get_error_status(self, e: Exception) -> int | None:
|
|
134
|
+
"""Extract HTTP status code from OpenAI exception."""
|
|
135
|
+
return getattr(e, "status_code", None) # pragma: no cover
|
|
136
|
+
|
|
112
137
|
def _choose_subprovider(
|
|
113
138
|
self, model_id: OpenAIModelId, messages: Sequence[Message]
|
|
114
139
|
) -> OpenAICompletionsProvider | OpenAIResponsesProvider:
|