instructor 1.2.2__tar.gz → 1.2.4__tar.gz
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.
- {instructor-1.2.2 → instructor-1.2.4}/PKG-INFO +6 -6
- {instructor-1.2.2 → instructor-1.2.4}/README.md +5 -5
- {instructor-1.2.2 → instructor-1.2.4}/instructor/client.py +28 -6
- {instructor-1.2.2 → instructor-1.2.4}/instructor/function_calls.py +13 -9
- {instructor-1.2.2 → instructor-1.2.4}/instructor/patch.py +4 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/retry.py +28 -14
- {instructor-1.2.2 → instructor-1.2.4}/instructor/utils.py +23 -5
- {instructor-1.2.2 → instructor-1.2.4}/pyproject.toml +1 -1
- {instructor-1.2.2 → instructor-1.2.4}/LICENSE +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/_types/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/_types/_alias.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/cli.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/files.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/hub.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/jobs.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/cli/usage.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/client_anthropic.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/client_cohere.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/client_groq.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/client_mistral.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/distil.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/citation.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/iterable.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/maybe.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/parallel.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/partial.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/simple_type.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/dsl/validators.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/exceptions.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/mode.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/process_response.py +0 -0
- {instructor-1.2.2 → instructor-1.2.4}/instructor/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: instructor
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: structured outputs for llm
|
|
5
5
|
Home-page: https://github.com/jxnl/instructor
|
|
6
6
|
License: MIT
|
|
@@ -54,9 +54,9 @@ Instructor is a Python library that makes it a breeze to work with structured ou
|
|
|
54
54
|
|
|
55
55
|
- **Response Models**: Specify Pydantic models to define the structure of your LLM outputs
|
|
56
56
|
- **Retry Management**: Easily configure the number of retry attempts for your requests
|
|
57
|
-
|
|
57
|
+
- **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
|
|
58
58
|
- **Streaming Support**: Work with Lists and Partial responses effortlessly
|
|
59
|
-
|
|
59
|
+
- **Flexible Backends**: Seamlessly integrate with various LLM providers beyond OpenAI
|
|
60
60
|
|
|
61
61
|
## Get Started in Minutes
|
|
62
62
|
|
|
@@ -201,7 +201,7 @@ assert resp.name == "Jason"
|
|
|
201
201
|
assert resp.age == 25
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
## Type are
|
|
204
|
+
## Type are inferred correctly
|
|
205
205
|
|
|
206
206
|
This was the dream of instructor but due to the patching of openai, it wasnt possible for me to get typing to work well. Now, with the new client, we can get typing to work well! We've also added a few `create_*` methods to make it easier to create iterables and partials, and to access the original completion.
|
|
207
207
|
|
|
@@ -229,7 +229,7 @@ user = client.chat.completions.create(
|
|
|
229
229
|
)
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
-
Now if you use a IDE, you can see the type is correctly
|
|
232
|
+
Now if you use a IDE, you can see the type is correctly inferred.
|
|
233
233
|
|
|
234
234
|

|
|
235
235
|
|
|
@@ -341,7 +341,7 @@ for user in user_stream:
|
|
|
341
341
|
# name='John Doe' age=30
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
Notice now that the type
|
|
344
|
+
Notice now that the type inferred is `Generator[User, None]`
|
|
345
345
|
|
|
346
346
|

|
|
347
347
|
|
|
@@ -11,9 +11,9 @@ Instructor is a Python library that makes it a breeze to work with structured ou
|
|
|
11
11
|
|
|
12
12
|
- **Response Models**: Specify Pydantic models to define the structure of your LLM outputs
|
|
13
13
|
- **Retry Management**: Easily configure the number of retry attempts for your requests
|
|
14
|
-
|
|
14
|
+
- **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
|
|
15
15
|
- **Streaming Support**: Work with Lists and Partial responses effortlessly
|
|
16
|
-
|
|
16
|
+
- **Flexible Backends**: Seamlessly integrate with various LLM providers beyond OpenAI
|
|
17
17
|
|
|
18
18
|
## Get Started in Minutes
|
|
19
19
|
|
|
@@ -158,7 +158,7 @@ assert resp.name == "Jason"
|
|
|
158
158
|
assert resp.age == 25
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
## Type are
|
|
161
|
+
## Type are inferred correctly
|
|
162
162
|
|
|
163
163
|
This was the dream of instructor but due to the patching of openai, it wasnt possible for me to get typing to work well. Now, with the new client, we can get typing to work well! We've also added a few `create_*` methods to make it easier to create iterables and partials, and to access the original completion.
|
|
164
164
|
|
|
@@ -186,7 +186,7 @@ user = client.chat.completions.create(
|
|
|
186
186
|
)
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
Now if you use a IDE, you can see the type is correctly
|
|
189
|
+
Now if you use a IDE, you can see the type is correctly inferred.
|
|
190
190
|
|
|
191
191
|

|
|
192
192
|
|
|
@@ -298,7 +298,7 @@ for user in user_stream:
|
|
|
298
298
|
# name='John Doe' age=30
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
-
Notice now that the type
|
|
301
|
+
Notice now that the type inferred is `Generator[User, None]`
|
|
302
302
|
|
|
303
303
|

|
|
304
304
|
|
|
@@ -67,6 +67,7 @@ class Instructor:
|
|
|
67
67
|
messages: List[ChatCompletionMessageParam],
|
|
68
68
|
max_retries: int = 3,
|
|
69
69
|
validation_context: dict | None = None,
|
|
70
|
+
strict: bool = True,
|
|
70
71
|
**kwargs,
|
|
71
72
|
) -> T:
|
|
72
73
|
kwargs = self.handle_kwargs(kwargs)
|
|
@@ -76,6 +77,7 @@ class Instructor:
|
|
|
76
77
|
messages=messages,
|
|
77
78
|
max_retries=max_retries,
|
|
78
79
|
validation_context=validation_context,
|
|
80
|
+
strict=strict,
|
|
79
81
|
**kwargs,
|
|
80
82
|
)
|
|
81
83
|
|
|
@@ -85,6 +87,7 @@ class Instructor:
|
|
|
85
87
|
messages: List[ChatCompletionMessageParam],
|
|
86
88
|
max_retries: int = 3,
|
|
87
89
|
validation_context: dict | None = None,
|
|
90
|
+
strict: bool = True,
|
|
88
91
|
**kwargs,
|
|
89
92
|
) -> Generator[T, None, None]:
|
|
90
93
|
assert self.provider != Provider.ANTHROPIC, "Anthropic doesn't support partial"
|
|
@@ -99,6 +102,7 @@ class Instructor:
|
|
|
99
102
|
response_model=response_model,
|
|
100
103
|
max_retries=max_retries,
|
|
101
104
|
validation_context=validation_context,
|
|
105
|
+
strict=strict,
|
|
102
106
|
**kwargs,
|
|
103
107
|
)
|
|
104
108
|
|
|
@@ -108,6 +112,7 @@ class Instructor:
|
|
|
108
112
|
response_model: Type[T],
|
|
109
113
|
max_retries: int = 3,
|
|
110
114
|
validation_context: dict | None = None,
|
|
115
|
+
strict: bool = True,
|
|
111
116
|
**kwargs,
|
|
112
117
|
) -> Iterable[T]:
|
|
113
118
|
assert self.provider != Provider.ANTHROPIC, "Anthropic doesn't support iterable"
|
|
@@ -121,6 +126,7 @@ class Instructor:
|
|
|
121
126
|
response_model=response_model,
|
|
122
127
|
max_retries=max_retries,
|
|
123
128
|
validation_context=validation_context,
|
|
129
|
+
strict=strict,
|
|
124
130
|
**kwargs,
|
|
125
131
|
)
|
|
126
132
|
|
|
@@ -130,6 +136,7 @@ class Instructor:
|
|
|
130
136
|
response_model: Type[T],
|
|
131
137
|
max_retries: int = 3,
|
|
132
138
|
validation_context: dict | None = None,
|
|
139
|
+
strict: bool = True,
|
|
133
140
|
**kwargs,
|
|
134
141
|
) -> Tuple[T, ChatCompletion | Any]:
|
|
135
142
|
kwargs = self.handle_kwargs(kwargs)
|
|
@@ -138,6 +145,7 @@ class Instructor:
|
|
|
138
145
|
response_model=response_model,
|
|
139
146
|
max_retries=max_retries,
|
|
140
147
|
validation_context=validation_context,
|
|
148
|
+
strict=strict,
|
|
141
149
|
**kwargs,
|
|
142
150
|
)
|
|
143
151
|
return model, model._raw_response
|
|
@@ -176,6 +184,7 @@ class AsyncInstructor(Instructor):
|
|
|
176
184
|
response_model: Type[T],
|
|
177
185
|
validation_context: dict | None = None,
|
|
178
186
|
max_retries: int = 3,
|
|
187
|
+
strict: bool = True,
|
|
179
188
|
**kwargs,
|
|
180
189
|
) -> T:
|
|
181
190
|
kwargs = self.handle_kwargs(kwargs)
|
|
@@ -184,6 +193,7 @@ class AsyncInstructor(Instructor):
|
|
|
184
193
|
validation_context=validation_context,
|
|
185
194
|
max_retries=max_retries,
|
|
186
195
|
messages=messages,
|
|
196
|
+
strict=strict,
|
|
187
197
|
**kwargs,
|
|
188
198
|
)
|
|
189
199
|
|
|
@@ -193,6 +203,7 @@ class AsyncInstructor(Instructor):
|
|
|
193
203
|
messages: List[ChatCompletionMessageParam],
|
|
194
204
|
validation_context: dict | None = None,
|
|
195
205
|
max_retries: int = 3,
|
|
206
|
+
strict: bool = True,
|
|
196
207
|
**kwargs,
|
|
197
208
|
) -> AsyncGenerator[T, None]:
|
|
198
209
|
assert self.provider != Provider.ANTHROPIC, "Anthropic doesn't support partial"
|
|
@@ -204,6 +215,7 @@ class AsyncInstructor(Instructor):
|
|
|
204
215
|
validation_context=validation_context,
|
|
205
216
|
max_retries=max_retries,
|
|
206
217
|
messages=messages,
|
|
218
|
+
strict=strict,
|
|
207
219
|
**kwargs,
|
|
208
220
|
):
|
|
209
221
|
yield item
|
|
@@ -214,6 +226,7 @@ class AsyncInstructor(Instructor):
|
|
|
214
226
|
messages: List[ChatCompletionMessageParam],
|
|
215
227
|
validation_context: dict | None = None,
|
|
216
228
|
max_retries: int = 3,
|
|
229
|
+
strict: bool = True,
|
|
217
230
|
**kwargs,
|
|
218
231
|
) -> AsyncGenerator[T, None]:
|
|
219
232
|
assert self.provider != Provider.ANTHROPIC, "Anthropic doesn't support iterable"
|
|
@@ -225,6 +238,7 @@ class AsyncInstructor(Instructor):
|
|
|
225
238
|
validation_context=validation_context,
|
|
226
239
|
max_retries=max_retries,
|
|
227
240
|
messages=messages,
|
|
241
|
+
strict=strict,
|
|
228
242
|
**kwargs,
|
|
229
243
|
):
|
|
230
244
|
yield item
|
|
@@ -235,6 +249,7 @@ class AsyncInstructor(Instructor):
|
|
|
235
249
|
messages: List[ChatCompletionMessageParam],
|
|
236
250
|
validation_context: dict | None = None,
|
|
237
251
|
max_retries: int = 3,
|
|
252
|
+
strict: bool = True,
|
|
238
253
|
**kwargs,
|
|
239
254
|
) -> Tuple[T, dict]:
|
|
240
255
|
kwargs = self.handle_kwargs(kwargs)
|
|
@@ -243,6 +258,7 @@ class AsyncInstructor(Instructor):
|
|
|
243
258
|
validation_context=validation_context,
|
|
244
259
|
max_retries=max_retries,
|
|
245
260
|
messages=messages,
|
|
261
|
+
strict=strict,
|
|
246
262
|
**kwargs,
|
|
247
263
|
)
|
|
248
264
|
return response, response._raw_response
|
|
@@ -269,11 +285,18 @@ def from_openai(
|
|
|
269
285
|
mode: instructor.Mode = instructor.Mode.TOOLS,
|
|
270
286
|
**kwargs,
|
|
271
287
|
) -> Instructor | AsyncInstructor:
|
|
272
|
-
|
|
288
|
+
if hasattr(client, "base_url"):
|
|
289
|
+
provider = get_provider(str(client.base_url))
|
|
290
|
+
else:
|
|
291
|
+
provider = Provider.OPENAI
|
|
292
|
+
|
|
293
|
+
if not isinstance(client, (openai.OpenAI, openai.AsyncOpenAI)):
|
|
294
|
+
import warnings
|
|
273
295
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
warnings.warn(
|
|
297
|
+
"Client should be an instance of openai.OpenAI or openai.AsyncOpenAI. "
|
|
298
|
+
"Unexpected behavior may occur with other client types."
|
|
299
|
+
)
|
|
277
300
|
|
|
278
301
|
if provider in {Provider.ANYSCALE, Provider.TOGETHER}:
|
|
279
302
|
assert mode in {
|
|
@@ -316,8 +339,7 @@ def from_litellm(
|
|
|
316
339
|
completion: Callable,
|
|
317
340
|
mode: instructor.Mode = instructor.Mode.TOOLS,
|
|
318
341
|
**kwargs,
|
|
319
|
-
) -> Instructor:
|
|
320
|
-
...
|
|
342
|
+
) -> Instructor: ...
|
|
321
343
|
|
|
322
344
|
|
|
323
345
|
@overload
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
from docstring_parser import parse
|
|
1
|
+
import logging
|
|
3
2
|
from functools import wraps
|
|
4
|
-
from
|
|
3
|
+
from typing import Annotated, Any, Dict, Optional, Type, TypeVar
|
|
4
|
+
|
|
5
|
+
from docstring_parser import parse
|
|
5
6
|
from openai.types.chat import ChatCompletion
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from instructor.utils import extract_json_from_codeblock
|
|
7
|
+
from pydantic import BaseModel, Field, TypeAdapter, create_model
|
|
8
|
+
|
|
9
9
|
from instructor.exceptions import IncompleteOutputException
|
|
10
10
|
from instructor.mode import Mode
|
|
11
|
-
import
|
|
12
|
-
|
|
11
|
+
from instructor.utils import extract_json_from_codeblock
|
|
13
12
|
|
|
14
13
|
T = TypeVar("T")
|
|
15
14
|
|
|
@@ -119,7 +118,12 @@ class OpenAISchema(BaseModel): # type: ignore[misc]
|
|
|
119
118
|
validation_context: Optional[Dict[str, Any]] = None,
|
|
120
119
|
strict: Optional[bool] = None,
|
|
121
120
|
) -> BaseModel:
|
|
122
|
-
|
|
121
|
+
tool_calls = [c.input for c in completion.content if c.type == "tool_use"]
|
|
122
|
+
|
|
123
|
+
tool_calls_validator = TypeAdapter(
|
|
124
|
+
Annotated[list, Field(min_length=1, max_length=1)]
|
|
125
|
+
)
|
|
126
|
+
tool_call = tool_calls_validator.validate_python(tool_calls)[0]
|
|
123
127
|
|
|
124
128
|
return cls.model_validate(tool_call, context=validation_context, strict=strict) # type:ignore
|
|
125
129
|
|
|
@@ -116,6 +116,7 @@ def patch(
|
|
|
116
116
|
response_model: Type[T_Model] = None,
|
|
117
117
|
validation_context: dict = None,
|
|
118
118
|
max_retries: int = 1,
|
|
119
|
+
strict: bool = True,
|
|
119
120
|
*args: T_ParamSpec.args,
|
|
120
121
|
**kwargs: T_ParamSpec.kwargs,
|
|
121
122
|
) -> T_Model:
|
|
@@ -129,6 +130,7 @@ def patch(
|
|
|
129
130
|
max_retries=max_retries,
|
|
130
131
|
args=args,
|
|
131
132
|
kwargs=new_kwargs,
|
|
133
|
+
strict=strict,
|
|
132
134
|
mode=mode,
|
|
133
135
|
) # type: ignore
|
|
134
136
|
return response
|
|
@@ -138,6 +140,7 @@ def patch(
|
|
|
138
140
|
response_model: Type[T_Model] = None,
|
|
139
141
|
validation_context: dict = None,
|
|
140
142
|
max_retries: int = 1,
|
|
143
|
+
strict: bool = True,
|
|
141
144
|
*args: T_ParamSpec.args,
|
|
142
145
|
**kwargs: T_ParamSpec.kwargs,
|
|
143
146
|
) -> T_Model:
|
|
@@ -150,6 +153,7 @@ def patch(
|
|
|
150
153
|
validation_context=validation_context,
|
|
151
154
|
max_retries=max_retries,
|
|
152
155
|
args=args,
|
|
156
|
+
strict=strict,
|
|
153
157
|
kwargs=new_kwargs,
|
|
154
158
|
mode=mode,
|
|
155
159
|
)
|
|
@@ -62,22 +62,27 @@ def reask_messages(response: ChatCompletion, mode: Mode, exception: Exception):
|
|
|
62
62
|
):
|
|
63
63
|
tool_use_id = content.id
|
|
64
64
|
|
|
65
|
-
assert tool_use_id is not None, "Tool use ID not found in the response"
|
|
66
65
|
yield {
|
|
67
66
|
"role": "assistant",
|
|
68
67
|
"content": assistant_content,
|
|
69
68
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
if tool_use_id is not None:
|
|
70
|
+
yield {
|
|
71
|
+
"role": "user",
|
|
72
|
+
"content": [
|
|
73
|
+
{
|
|
74
|
+
"type": "tool_result",
|
|
75
|
+
"tool_use_id": tool_use_id,
|
|
76
|
+
"content": f"Validation Error found:\n{exception}\nRecall the function correctly, fix the errors",
|
|
77
|
+
"is_error": True,
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
}
|
|
81
|
+
else:
|
|
82
|
+
yield {
|
|
83
|
+
"role": "user",
|
|
84
|
+
"content": f"Validation Error due to no tool invocation:\n{exception}\nRecall the function correctly, fix the errors",
|
|
85
|
+
}
|
|
81
86
|
return
|
|
82
87
|
if mode == Mode.ANTHROPIC_JSON:
|
|
83
88
|
from anthropic.types import Message
|
|
@@ -128,6 +133,10 @@ def retry_sync(
|
|
|
128
133
|
mode: Mode = Mode.TOOLS,
|
|
129
134
|
) -> T_Model:
|
|
130
135
|
total_usage = CompletionUsage(completion_tokens=0, prompt_tokens=0, total_tokens=0)
|
|
136
|
+
if mode in {Mode.ANTHROPIC_TOOLS, Mode.ANTHROPIC_JSON}:
|
|
137
|
+
from anthropic.types import Usage as AnthropicUsage
|
|
138
|
+
|
|
139
|
+
total_usage = AnthropicUsage(input_tokens=0, output_tokens=0)
|
|
131
140
|
|
|
132
141
|
# If max_retries is int, then create a Retrying object
|
|
133
142
|
if isinstance(max_retries, int):
|
|
@@ -189,6 +198,11 @@ async def retry_async(
|
|
|
189
198
|
mode: Mode = Mode.TOOLS,
|
|
190
199
|
) -> T:
|
|
191
200
|
total_usage = CompletionUsage(completion_tokens=0, prompt_tokens=0, total_tokens=0)
|
|
201
|
+
if mode in {Mode.ANTHROPIC_TOOLS, Mode.ANTHROPIC_JSON}:
|
|
202
|
+
from anthropic.types import Usage as AnthropicUsage
|
|
203
|
+
|
|
204
|
+
total_usage = AnthropicUsage(input_tokens=0, output_tokens=0)
|
|
205
|
+
|
|
192
206
|
# If max_retries is int, then create a AsyncRetrying object
|
|
193
207
|
if isinstance(max_retries, int):
|
|
194
208
|
logger.debug(f"max_retries: {max_retries}")
|
|
@@ -227,7 +241,7 @@ async def retry_async(
|
|
|
227
241
|
raise InstructorRetryException(
|
|
228
242
|
e,
|
|
229
243
|
last_completion=response,
|
|
230
|
-
n_attempts=
|
|
244
|
+
n_attempts=attempt.retry_state.attempt_number,
|
|
231
245
|
messages=kwargs["messages"],
|
|
232
246
|
total_usage=total_usage,
|
|
233
247
|
) from e
|
|
@@ -236,7 +250,7 @@ async def retry_async(
|
|
|
236
250
|
raise InstructorRetryException(
|
|
237
251
|
e,
|
|
238
252
|
last_completion=response,
|
|
239
|
-
n_attempts=
|
|
253
|
+
n_attempts=attempt.retry_state.attempt_number,
|
|
240
254
|
messages=kwargs["messages"],
|
|
241
255
|
total_usage=total_usage,
|
|
242
256
|
) from e
|
|
@@ -2,16 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
|
+
import logging
|
|
5
6
|
from typing import Callable, Generator, Iterable, AsyncGenerator, TypeVar
|
|
6
7
|
|
|
7
8
|
from pydantic import BaseModel
|
|
8
|
-
|
|
9
|
+
from openai.types import CompletionUsage as OpenAIUsage
|
|
9
10
|
from openai.types.chat import (
|
|
10
11
|
ChatCompletion,
|
|
11
12
|
ChatCompletionMessage,
|
|
12
13
|
ChatCompletionMessageParam,
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
logger = logging.getLogger("instructor")
|
|
15
17
|
T_Model = TypeVar("T_Model", bound=BaseModel)
|
|
16
18
|
|
|
17
19
|
from enum import Enum
|
|
@@ -93,11 +95,27 @@ async def extract_json_from_stream_async(
|
|
|
93
95
|
|
|
94
96
|
|
|
95
97
|
def update_total_usage(response: T_Model, total_usage) -> T_Model | ChatCompletion:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
total_usage.
|
|
99
|
-
total_usage.
|
|
98
|
+
response_usage = getattr(response, "usage", None)
|
|
99
|
+
if isinstance(response_usage, OpenAIUsage):
|
|
100
|
+
total_usage.completion_tokens += response_usage.completion_tokens or 0
|
|
101
|
+
total_usage.prompt_tokens += response_usage.prompt_tokens or 0
|
|
102
|
+
total_usage.total_tokens += response_usage.total_tokens or 0
|
|
100
103
|
response.usage = total_usage # Replace each response usage with the total usage
|
|
104
|
+
return response
|
|
105
|
+
|
|
106
|
+
# Anthropic usage
|
|
107
|
+
try:
|
|
108
|
+
from anthropic.types import Usage as AnthropicUsage
|
|
109
|
+
|
|
110
|
+
if isinstance(response_usage, AnthropicUsage):
|
|
111
|
+
total_usage.input_tokens += response_usage.input_tokens or 0
|
|
112
|
+
total_usage.output_tokens += response_usage.output_tokens or 0
|
|
113
|
+
response.usage = total_usage
|
|
114
|
+
return response
|
|
115
|
+
except ImportError:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
logger.debug("No compatible response.usage found, token usage not updated.")
|
|
101
119
|
return response
|
|
102
120
|
|
|
103
121
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|