instructor 1.2.0__tar.gz → 1.2.2__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.0 → instructor-1.2.2}/PKG-INFO +4 -2
  2. {instructor-1.2.0 → instructor-1.2.2}/instructor/__init__.py +5 -0
  3. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/usage.py +5 -5
  4. {instructor-1.2.0 → instructor-1.2.2}/instructor/client.py +3 -1
  5. {instructor-1.2.0 → instructor-1.2.2}/instructor/client_anthropic.py +4 -2
  6. {instructor-1.2.0 → instructor-1.2.2}/instructor/client_cohere.py +4 -2
  7. {instructor-1.2.0 → instructor-1.2.2}/instructor/client_groq.py +4 -2
  8. instructor-1.2.2/instructor/client_mistral.py +57 -0
  9. {instructor-1.2.0 → instructor-1.2.2}/instructor/patch.py +12 -6
  10. {instructor-1.2.0 → instructor-1.2.2}/instructor/retry.py +45 -6
  11. {instructor-1.2.0 → instructor-1.2.2}/instructor/utils.py +3 -0
  12. {instructor-1.2.0 → instructor-1.2.2}/pyproject.toml +6 -3
  13. {instructor-1.2.0 → instructor-1.2.2}/LICENSE +0 -0
  14. {instructor-1.2.0 → instructor-1.2.2}/README.md +0 -0
  15. {instructor-1.2.0 → instructor-1.2.2}/instructor/_types/__init__.py +0 -0
  16. {instructor-1.2.0 → instructor-1.2.2}/instructor/_types/_alias.py +0 -0
  17. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/__init__.py +0 -0
  18. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/cli.py +0 -0
  19. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/files.py +0 -0
  20. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/hub.py +0 -0
  21. {instructor-1.2.0 → instructor-1.2.2}/instructor/cli/jobs.py +0 -0
  22. {instructor-1.2.0 → instructor-1.2.2}/instructor/distil.py +0 -0
  23. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/__init__.py +0 -0
  24. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/citation.py +0 -0
  25. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/iterable.py +0 -0
  26. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/maybe.py +0 -0
  27. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/parallel.py +0 -0
  28. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/partial.py +0 -0
  29. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/simple_type.py +0 -0
  30. {instructor-1.2.0 → instructor-1.2.2}/instructor/dsl/validators.py +0 -0
  31. {instructor-1.2.0 → instructor-1.2.2}/instructor/exceptions.py +0 -0
  32. {instructor-1.2.0 → instructor-1.2.2}/instructor/function_calls.py +0 -0
  33. {instructor-1.2.0 → instructor-1.2.2}/instructor/mode.py +0 -0
  34. {instructor-1.2.0 → instructor-1.2.2}/instructor/process_response.py +0 -0
  35. {instructor-1.2.0 → instructor-1.2.2}/instructor/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: instructor
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: structured outputs for llm
5
5
  Home-page: https://github.com/jxnl/instructor
6
6
  License: MIT
@@ -16,15 +16,17 @@ Classifier: Programming Language :: Python :: 3.12
16
16
  Provides-Extra: anthropic
17
17
  Provides-Extra: cohere
18
18
  Provides-Extra: groq
19
+ Provides-Extra: mistralai
19
20
  Provides-Extra: test-docs
20
21
  Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
21
22
  Requires-Dist: anthropic (>=0.23.1,<0.24.0) ; extra == "anthropic" or extra == "test-docs"
22
23
  Requires-Dist: cohere (>=5.1.8,<6.0.0) ; extra == "cohere" or extra == "test-docs"
23
24
  Requires-Dist: diskcache (>=5.6.3,<6.0.0) ; extra == "test-docs"
24
- Requires-Dist: docstring-parser (>=0.15,<0.16)
25
+ Requires-Dist: docstring-parser (>=0.16,<0.17)
25
26
  Requires-Dist: fastapi (>=0.109.2,<0.110.0) ; extra == "test-docs"
26
27
  Requires-Dist: groq (>=0.4.2,<0.5.0) ; extra == "groq" or extra == "test-docs"
27
28
  Requires-Dist: litellm (>=1.0.0,<2.0.0) ; extra == "test-docs"
29
+ Requires-Dist: mistralai (>=0.1.8,<0.2.0) ; extra == "test-docs" or extra == "mistralai"
28
30
  Requires-Dist: openai (>=1.1.0,<2.0.0)
29
31
  Requires-Dist: pandas (>=2.2.0,<3.0.0) ; extra == "test-docs"
30
32
  Requires-Dist: pydantic (==2.7.0)
@@ -57,6 +57,11 @@ if importlib.util.find_spec("groq") is not None:
57
57
 
58
58
  __all__ += ["from_groq"]
59
59
 
60
+ if importlib.util.find_spec("mistralai") is not None:
61
+ from .client_mistral import from_mistral
62
+
63
+ __all__ += ["from_mistral"]
64
+
60
65
  if importlib.util.find_spec("cohere") is not None:
61
66
  from .client_cohere import from_cohere
62
67
 
@@ -114,11 +114,11 @@ def calculate_cost(
114
114
 
115
115
  def group_and_sum_by_date_and_snapshot(usage_data: List[Dict[str, Any]]) -> Table:
116
116
  """Group and sum the usage data by date and snapshot, including costs."""
117
- summary: DefaultDict[str, DefaultDict[str, Dict[str, Union[int, float]]]] = (
118
- defaultdict(
119
- lambda: defaultdict(
120
- lambda: {"total_requests": 0, "total_tokens": 0, "total_cost": 0.0}
121
- )
117
+ summary: DefaultDict[
118
+ str, DefaultDict[str, Dict[str, Union[int, float]]]
119
+ ] = defaultdict(
120
+ lambda: defaultdict(
121
+ lambda: {"total_requests": 0, "total_tokens": 0, "total_cost": 0.0}
122
122
  )
123
123
  )
124
124
 
@@ -280,6 +280,7 @@ def from_openai(
280
280
  instructor.Mode.TOOLS,
281
281
  instructor.Mode.JSON,
282
282
  instructor.Mode.JSON_SCHEMA,
283
+ instructor.Mode.MD_JSON,
283
284
  }
284
285
 
285
286
  if provider in {Provider.OPENAI}:
@@ -315,7 +316,8 @@ def from_litellm(
315
316
  completion: Callable,
316
317
  mode: instructor.Mode = instructor.Mode.TOOLS,
317
318
  **kwargs,
318
- ) -> Instructor: ...
319
+ ) -> Instructor:
320
+ ...
319
321
 
320
322
 
321
323
  @overload
@@ -13,7 +13,8 @@ def from_anthropic(
13
13
  | anthropic.AnthropicVertex,
14
14
  mode: instructor.Mode = instructor.Mode.ANTHROPIC_JSON,
15
15
  **kwargs,
16
- ) -> instructor.Instructor: ...
16
+ ) -> instructor.Instructor:
17
+ ...
17
18
 
18
19
 
19
20
  @overload
@@ -23,7 +24,8 @@ def from_anthropic(
23
24
  | anthropic.AsyncAnthropicVertex,
24
25
  mode: instructor.Mode = instructor.Mode.ANTHROPIC_JSON,
25
26
  **kwargs,
26
- ) -> instructor.Instructor: ...
27
+ ) -> instructor.Instructor:
28
+ ...
27
29
 
28
30
 
29
31
  def from_anthropic(
@@ -23,7 +23,8 @@ def from_cohere(
23
23
  client: cohere.Client,
24
24
  mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
25
25
  **kwargs,
26
- ) -> instructor.Instructor: ...
26
+ ) -> instructor.Instructor:
27
+ ...
27
28
 
28
29
 
29
30
  @overload
@@ -31,7 +32,8 @@ def from_cohere(
31
32
  client: cohere.AsyncClient,
32
33
  mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
33
34
  **kwargs,
34
- ) -> instructor.AsyncInstructor: ...
35
+ ) -> instructor.AsyncInstructor:
36
+ ...
35
37
 
36
38
 
37
39
  def from_cohere(
@@ -11,7 +11,8 @@ def from_groq(
11
11
  client: groq.Groq,
12
12
  mode: instructor.Mode = instructor.Mode.TOOLS,
13
13
  **kwargs,
14
- ) -> instructor.Instructor: ...
14
+ ) -> instructor.Instructor:
15
+ ...
15
16
 
16
17
 
17
18
  @overload
@@ -19,7 +20,8 @@ def from_groq(
19
20
  client: groq.AsyncGroq,
20
21
  mode: instructor.Mode = instructor.Mode.TOOLS,
21
22
  **kwargs,
22
- ) -> instructor.Instructor: ...
23
+ ) -> instructor.Instructor:
24
+ ...
23
25
 
24
26
 
25
27
  def from_groq(
@@ -0,0 +1,57 @@
1
+ # Future imports to ensure compatibility with Python 3.9
2
+ from __future__ import annotations
3
+
4
+ import mistralai.client
5
+ import mistralai.async_client as mistralaiasynccli
6
+ import instructor
7
+ from typing import overload
8
+
9
+
10
+ @overload
11
+ def from_mistral(
12
+ client: mistralai.client.MistralClient,
13
+ mode: instructor.Mode = instructor.Mode.MISTRAL_TOOLS,
14
+ **kwargs,
15
+ ) -> instructor.Instructor: ...
16
+
17
+
18
+ @overload
19
+ def from_mistral(
20
+ client: mistralaiasynccli.MistralAsyncClient,
21
+ mode: instructor.Mode = instructor.Mode.MISTRAL_TOOLS,
22
+ **kwargs,
23
+ ) -> instructor.AsyncInstructor: ...
24
+
25
+
26
+ def from_mistral(
27
+ client: mistralai.client.MistralClient | mistralaiasynccli.MistralAsyncClient,
28
+ mode: instructor.Mode = instructor.Mode.MISTRAL_TOOLS,
29
+ **kwargs,
30
+ ) -> instructor.Instructor | instructor.AsyncInstructor:
31
+ assert mode in {
32
+ instructor.Mode.MISTRAL_TOOLS,
33
+ }, "Mode be one of {instructor.Mode.MISTRAL_TOOLS}"
34
+
35
+ assert isinstance(
36
+ client, (mistralai.client.MistralClient, mistralaiasynccli.MistralAsyncClient)
37
+ ), "Client must be an instance of mistralai.client.MistralClient or mistralai.async_cli.MistralAsyncClient"
38
+
39
+ if isinstance(client, mistralai.client.MistralClient):
40
+
41
+ return instructor.Instructor(
42
+ client=client,
43
+ create=instructor.patch(create=client.chat, mode=mode),
44
+ provider=instructor.Provider.MISTRAL,
45
+ mode=mode,
46
+ **kwargs,
47
+ )
48
+
49
+ else:
50
+
51
+ return instructor.AsyncInstructor(
52
+ client=client,
53
+ create=instructor.patch(create=client.chat, mode=mode),
54
+ provider=instructor.Provider.MISTRAL,
55
+ mode=mode,
56
+ **kwargs,
57
+ )
@@ -36,7 +36,8 @@ class InstructorChatCompletionCreate(Protocol):
36
36
  max_retries: int = 1,
37
37
  *args: T_ParamSpec.args,
38
38
  **kwargs: T_ParamSpec.kwargs,
39
- ) -> T_Model: ...
39
+ ) -> T_Model:
40
+ ...
40
41
 
41
42
 
42
43
  class AsyncInstructorChatCompletionCreate(Protocol):
@@ -47,35 +48,40 @@ class AsyncInstructorChatCompletionCreate(Protocol):
47
48
  max_retries: int = 1,
48
49
  *args: T_ParamSpec.args,
49
50
  **kwargs: T_ParamSpec.kwargs,
50
- ) -> T_Model: ...
51
+ ) -> T_Model:
52
+ ...
51
53
 
52
54
 
53
55
  @overload
54
56
  def patch(
55
57
  client: OpenAI,
56
58
  mode: Mode = Mode.TOOLS,
57
- ) -> OpenAI: ...
59
+ ) -> OpenAI:
60
+ ...
58
61
 
59
62
 
60
63
  @overload
61
64
  def patch(
62
65
  client: AsyncOpenAI,
63
66
  mode: Mode = Mode.TOOLS,
64
- ) -> AsyncOpenAI: ...
67
+ ) -> AsyncOpenAI:
68
+ ...
65
69
 
66
70
 
67
71
  @overload
68
72
  def patch(
69
73
  create: Callable[T_ParamSpec, T_Retval],
70
74
  mode: Mode = Mode.TOOLS,
71
- ) -> InstructorChatCompletionCreate: ...
75
+ ) -> InstructorChatCompletionCreate:
76
+ ...
72
77
 
73
78
 
74
79
  @overload
75
80
  def patch(
76
81
  create: Awaitable[T_Retval],
77
82
  mode: Mode = Mode.TOOLS,
78
- ) -> InstructorChatCompletionCreate: ...
83
+ ) -> InstructorChatCompletionCreate:
84
+ ...
79
85
 
80
86
 
81
87
  def patch(
@@ -30,6 +30,23 @@ T_ParamSpec = ParamSpec("T_ParamSpec")
30
30
  T = TypeVar("T")
31
31
 
32
32
 
33
+ class InstructorRetryException(Exception):
34
+ def __init__(
35
+ self,
36
+ *args,
37
+ last_completion,
38
+ messages: list,
39
+ n_attempts: int,
40
+ total_usage,
41
+ **kwargs,
42
+ ):
43
+ self.last_completion = last_completion
44
+ self.messages = messages
45
+ self.n_attempts = n_attempts
46
+ self.total_usage = total_usage
47
+ super().__init__(*args, **kwargs)
48
+
49
+
33
50
  def reask_messages(response: ChatCompletion, mode: Mode, exception: Exception):
34
51
  if mode == Mode.ANTHROPIC_TOOLS:
35
52
  # The original response
@@ -144,10 +161,21 @@ def retry_sync(
144
161
  kwargs["messages"] = merge_consecutive_messages(
145
162
  kwargs["messages"]
146
163
  )
147
- raise e
164
+ raise InstructorRetryException(
165
+ e,
166
+ last_completion=response,
167
+ n_attempts=attempt.retry_state.attempt_number,
168
+ messages=kwargs["messages"],
169
+ total_usage=total_usage,
170
+ ) from e
148
171
  except RetryError as e:
149
- logger.exception(f"Failed after retries: {e.last_attempt.exception}")
150
- raise e.last_attempt.exception from e
172
+ raise InstructorRetryException(
173
+ e,
174
+ last_completion=response,
175
+ n_attempts=attempt.retry_state.attempt_number,
176
+ messages=kwargs["messages"],
177
+ total_usage=total_usage,
178
+ ) from e
151
179
 
152
180
 
153
181
  async def retry_async(
@@ -161,7 +189,6 @@ async def retry_async(
161
189
  mode: Mode = Mode.TOOLS,
162
190
  ) -> T:
163
191
  total_usage = CompletionUsage(completion_tokens=0, prompt_tokens=0, total_tokens=0)
164
-
165
192
  # If max_retries is int, then create a AsyncRetrying object
166
193
  if isinstance(max_retries, int):
167
194
  logger.debug(f"max_retries: {max_retries}")
@@ -197,7 +224,19 @@ async def retry_async(
197
224
  kwargs["messages"] = merge_consecutive_messages(
198
225
  kwargs["messages"]
199
226
  )
200
- raise e
227
+ raise InstructorRetryException(
228
+ e,
229
+ last_completion=response,
230
+ n_attempts=e.attempt_number,
231
+ messages=kwargs["messages"],
232
+ total_usage=total_usage,
233
+ ) from e
201
234
  except RetryError as e:
202
235
  logger.exception(f"Failed after retries: {e.last_attempt.exception}")
203
- raise e.last_attempt.exception from e
236
+ raise InstructorRetryException(
237
+ e,
238
+ last_completion=response,
239
+ n_attempts=e.attempt_number,
240
+ messages=kwargs["messages"],
241
+ total_usage=total_usage,
242
+ ) from e
@@ -23,6 +23,7 @@ class Provider(Enum):
23
23
  ANYSCALE = "anyscale"
24
24
  TOGETHER = "together"
25
25
  GROQ = "groq"
26
+ MISTRAL = "mistral"
26
27
  COHERE = "cohere"
27
28
  UNKNOWN = "unknown"
28
29
 
@@ -38,6 +39,8 @@ def get_provider(base_url: str) -> Provider:
38
39
  return Provider.GROQ
39
40
  elif "openai" in str(base_url):
40
41
  return Provider.OPENAI
42
+ elif "mistral" in str(base_url):
43
+ return Provider.MISTRAL
41
44
  elif "cohere" in str(base_url):
42
45
  return Provider.COHERE
43
46
  return Provider.UNKNOWN
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "instructor"
3
- version = "1.2.0"
3
+ version = "1.2.2"
4
4
  description = "structured outputs for llm"
5
5
  authors = ["Jason Liu <jason@jxnl.co>"]
6
6
  license = "MIT"
@@ -12,7 +12,7 @@ repository = "https://github.com/jxnl/instructor"
12
12
  python = "^3.9"
13
13
  openai = "^1.1.0"
14
14
  pydantic = "2.7.0"
15
- docstring-parser = "^0.15"
15
+ docstring-parser = "^0.16"
16
16
  typer = ">=0.9.0,<1.0.0"
17
17
  rich = "^13.7.0"
18
18
  aiohttp = "^3.9.1"
@@ -31,12 +31,14 @@ anthropic = { version = "^0.23.1", optional = true }
31
31
  xmltodict = { version = "^0.13.0", optional = true }
32
32
  groq = { version = "^0.4.2", optional = true }
33
33
  cohere = { version = "^5.1.8", optional = true }
34
+ mistralai = { version = "^0.1.8", optional = true }
34
35
 
35
36
  [tool.poetry.extras]
36
37
  anthropic = ["anthropic", "xmltodict"]
37
38
  groq = ["groq"]
38
39
  cohere = ["cohere"]
39
- test-docs = ["fastapi", "redis", "diskcache", "pandas", "tabulate", "pydantic_extra_types", "litellm", "anthropic", "groq", "cohere"]
40
+ test-docs = ["fastapi", "redis", "diskcache", "pandas", "tabulate", "pydantic_extra_types", "litellm", "anthropic", "groq", "cohere", "mistralai"]
41
+ mistralai = ["mistralai"]
40
42
 
41
43
  [tool.poetry.scripts]
42
44
  instructor = "instructor.cli.cli:app"
@@ -74,6 +76,7 @@ xmltodict = "^0.13.0"
74
76
  groq = "^0.4.2"
75
77
  phonenumbers = "^8.13.33"
76
78
  cohere = "^5.1.8"
79
+ mistralai = "^0.1.8"
77
80
 
78
81
  [build-system]
79
82
  requires = ["poetry-core"]
File without changes
File without changes