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.
Files changed (35) hide show
  1. {instructor-1.2.2 → instructor-1.2.3}/PKG-INFO +6 -6
  2. {instructor-1.2.2 → instructor-1.2.3}/README.md +5 -5
  3. {instructor-1.2.2 → instructor-1.2.3}/instructor/client.py +5 -3
  4. {instructor-1.2.2 → instructor-1.2.3}/instructor/function_calls.py +13 -9
  5. {instructor-1.2.2 → instructor-1.2.3}/instructor/retry.py +28 -14
  6. {instructor-1.2.2 → instructor-1.2.3}/instructor/utils.py +23 -5
  7. {instructor-1.2.2 → instructor-1.2.3}/pyproject.toml +1 -1
  8. {instructor-1.2.2 → instructor-1.2.3}/LICENSE +0 -0
  9. {instructor-1.2.2 → instructor-1.2.3}/instructor/__init__.py +0 -0
  10. {instructor-1.2.2 → instructor-1.2.3}/instructor/_types/__init__.py +0 -0
  11. {instructor-1.2.2 → instructor-1.2.3}/instructor/_types/_alias.py +0 -0
  12. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/__init__.py +0 -0
  13. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/cli.py +0 -0
  14. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/files.py +0 -0
  15. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/hub.py +0 -0
  16. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/jobs.py +0 -0
  17. {instructor-1.2.2 → instructor-1.2.3}/instructor/cli/usage.py +0 -0
  18. {instructor-1.2.2 → instructor-1.2.3}/instructor/client_anthropic.py +0 -0
  19. {instructor-1.2.2 → instructor-1.2.3}/instructor/client_cohere.py +0 -0
  20. {instructor-1.2.2 → instructor-1.2.3}/instructor/client_groq.py +0 -0
  21. {instructor-1.2.2 → instructor-1.2.3}/instructor/client_mistral.py +0 -0
  22. {instructor-1.2.2 → instructor-1.2.3}/instructor/distil.py +0 -0
  23. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/__init__.py +0 -0
  24. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/citation.py +0 -0
  25. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/iterable.py +0 -0
  26. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/maybe.py +0 -0
  27. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/parallel.py +0 -0
  28. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/partial.py +0 -0
  29. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/simple_type.py +0 -0
  30. {instructor-1.2.2 → instructor-1.2.3}/instructor/dsl/validators.py +0 -0
  31. {instructor-1.2.2 → instructor-1.2.3}/instructor/exceptions.py +0 -0
  32. {instructor-1.2.2 → instructor-1.2.3}/instructor/mode.py +0 -0
  33. {instructor-1.2.2 → instructor-1.2.3}/instructor/patch.py +0 -0
  34. {instructor-1.2.2 → instructor-1.2.3}/instructor/process_response.py +0 -0
  35. {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.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
- **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
57
+ - **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
58
58
  - **Streaming Support**: Work with Lists and Partial responses effortlessly
59
- **Flexible Backends**: Seamlessly integrate with various LLM providers beyond OpenAI
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 infered correctly
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 infered.
232
+ Now if you use a IDE, you can see the type is correctly inferred.
233
233
 
234
234
  ![type](./docs/blog/posts/img/type.png)
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 infered is `Generator[User, None]`
344
+ Notice now that the type inferred is `Generator[User, None]`
345
345
 
346
346
  ![generator](./docs/blog/posts/img/generator.png)
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
- **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
14
+ - **Validation**: Ensure LLM responses conform to your expectations with Pydantic validation
15
15
  - **Streaming Support**: Work with Lists and Partial responses effortlessly
16
- **Flexible Backends**: Seamlessly integrate with various LLM providers beyond OpenAI
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 infered correctly
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 infered.
189
+ Now if you use a IDE, you can see the type is correctly inferred.
190
190
 
191
191
  ![type](./docs/blog/posts/img/type.png)
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 infered is `Generator[User, None]`
301
+ Notice now that the type inferred is `Generator[User, None]`
302
302
 
303
303
  ![generator](./docs/blog/posts/img/generator.png)
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
- provider = get_provider(str(client.base_url))
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
- from typing import Any, Dict, Optional, Type, TypeVar
2
- from docstring_parser import parse
1
+ import logging
3
2
  from functools import wraps
4
- from pydantic import BaseModel, create_model
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 typing import Any, Dict, Optional, Type
7
- from instructor.mode import Mode
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 logging
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
- tool_call = [c.input for c in completion.content if c.type == "tool_use"][0]
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
- 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
- }
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=e.attempt_number,
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=e.attempt_number,
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
- if isinstance(response, ChatCompletion) and response.usage is not None:
97
- total_usage.completion_tokens += response.usage.completion_tokens or 0
98
- total_usage.prompt_tokens += response.usage.prompt_tokens or 0
99
- total_usage.total_tokens += response.usage.total_tokens or 0
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
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "instructor"
3
- version = "1.2.2"
3
+ version = "1.2.3"
4
4
  description = "structured outputs for llm"
5
5
  authors = ["Jason Liu <jason@jxnl.co>"]
6
6
  license = "MIT"
File without changes