instructor 1.2.2__tar.gz → 1.2.3__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.3}/PKG-INFO +6 -6
- {instructor-1.2.2 → instructor-1.2.3}/README.md +5 -5
- {instructor-1.2.2 → instructor-1.2.3}/instructor/client.py +5 -3
- {instructor-1.2.2 → instructor-1.2.3}/instructor/function_calls.py +13 -9
- {instructor-1.2.2 → instructor-1.2.3}/instructor/retry.py +28 -14
- {instructor-1.2.2 → instructor-1.2.3}/instructor/utils.py +23 -5
- {instructor-1.2.2 → instructor-1.2.3}/pyproject.toml +1 -1
- {instructor-1.2.2 → instructor-1.2.3}/LICENSE +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/_types/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/_types/_alias.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/cli.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/files.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/hub.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/jobs.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/usage.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/client_anthropic.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/client_cohere.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/client_groq.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/client_mistral.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/distil.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/__init__.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/citation.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/iterable.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/maybe.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/parallel.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/partial.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/simple_type.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/validators.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/exceptions.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/mode.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/patch.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/instructor/process_response.py +0 -0
- {instructor-1.2.2 → instructor-1.2.3}/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.3
|
|
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
|
|
|
@@ -269,7 +269,10 @@ def from_openai(
|
|
|
269
269
|
mode: instructor.Mode = instructor.Mode.TOOLS,
|
|
270
270
|
**kwargs,
|
|
271
271
|
) -> Instructor | AsyncInstructor:
|
|
272
|
-
|
|
272
|
+
if hasattr(client, "base_url"):
|
|
273
|
+
provider = get_provider(str(client.base_url))
|
|
274
|
+
else:
|
|
275
|
+
provider = Provider.OPENAI
|
|
273
276
|
|
|
274
277
|
assert isinstance(
|
|
275
278
|
client, (openai.OpenAI, openai.AsyncOpenAI)
|
|
@@ -316,8 +319,7 @@ def from_litellm(
|
|
|
316
319
|
completion: Callable,
|
|
317
320
|
mode: instructor.Mode = instructor.Mode.TOOLS,
|
|
318
321
|
**kwargs,
|
|
319
|
-
) -> Instructor:
|
|
320
|
-
...
|
|
322
|
+
) -> Instructor: ...
|
|
321
323
|
|
|
322
324
|
|
|
323
325
|
@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
|
|
|
@@ -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
|
|
File without changes
|