instructor 1.3.2__tar.gz → 1.3.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.3.2 → instructor-1.3.4}/PKG-INFO +55 -13
- {instructor-1.3.2 → instructor-1.3.4}/README.md +49 -11
- {instructor-1.3.2 → instructor-1.3.4}/instructor/__init__.py +5 -0
- instructor-1.3.4/instructor/batch.py +74 -0
- instructor-1.3.4/instructor/cli/batch.py +101 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/cli.py +4 -2
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/usage.py +5 -5
- {instructor-1.3.2 → instructor-1.3.4}/instructor/client.py +9 -20
- {instructor-1.3.2 → instructor-1.3.4}/instructor/client_anthropic.py +6 -11
- {instructor-1.3.2 → instructor-1.3.4}/instructor/client_cohere.py +2 -4
- instructor-1.3.4/instructor/client_gemini.py +61 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/client_groq.py +2 -4
- {instructor-1.3.2 → instructor-1.3.4}/instructor/client_mistral.py +2 -4
- instructor-1.3.4/instructor/client_vertexai.py +118 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/iterable.py +2 -2
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/parallel.py +14 -7
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/partial.py +9 -6
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/simple_type.py +24 -4
- instructor-1.3.4/instructor/exceptions.py +34 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/function_calls.py +38 -4
- {instructor-1.3.2 → instructor-1.3.4}/instructor/mode.py +3 -1
- {instructor-1.3.2 → instructor-1.3.4}/instructor/patch.py +6 -12
- {instructor-1.3.2 → instructor-1.3.4}/instructor/process_response.py +36 -3
- {instructor-1.3.2 → instructor-1.3.4}/instructor/retry.py +30 -33
- {instructor-1.3.2 → instructor-1.3.4}/instructor/utils.py +14 -2
- {instructor-1.3.2 → instructor-1.3.4}/pyproject.toml +31 -7
- instructor-1.3.2/instructor/client_gemini.py +0 -31
- instructor-1.3.2/instructor/exceptions.py +0 -13
- {instructor-1.3.2 → instructor-1.3.4}/LICENSE +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/_types/__init__.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/_types/_alias.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/__init__.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/files.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/hub.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/cli/jobs.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/distil.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/__init__.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/citation.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/maybe.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/dsl/validators.py +0 -0
- {instructor-1.3.2 → instructor-1.3.4}/instructor/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: instructor
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: structured outputs for llm
|
|
5
5
|
Home-page: https://github.com/jxnl/instructor
|
|
6
6
|
License: MIT
|
|
@@ -20,14 +20,18 @@ Provides-Extra: groq
|
|
|
20
20
|
Provides-Extra: litellm
|
|
21
21
|
Provides-Extra: mistralai
|
|
22
22
|
Provides-Extra: test-docs
|
|
23
|
+
Provides-Extra: vertexai
|
|
23
24
|
Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
|
|
24
|
-
Requires-Dist: anthropic (>=0.
|
|
25
|
+
Requires-Dist: anthropic (>=0.27.0,<0.28.0) ; extra == "anthropic" or extra == "test-docs"
|
|
25
26
|
Requires-Dist: cohere (>=5.1.8,<6.0.0) ; extra == "cohere" or extra == "test-docs"
|
|
26
27
|
Requires-Dist: diskcache (>=5.6.3,<6.0.0) ; extra == "test-docs"
|
|
27
28
|
Requires-Dist: docstring-parser (>=0.16,<0.17)
|
|
28
29
|
Requires-Dist: fastapi (>=0.109.2,<0.110.0) ; extra == "test-docs"
|
|
30
|
+
Requires-Dist: google-cloud-aiplatform (>=1.53.0,<2.0.0) ; extra == "vertexai"
|
|
29
31
|
Requires-Dist: google-generativeai (>=0.5.4,<0.6.0) ; extra == "google-generativeai"
|
|
30
32
|
Requires-Dist: groq (>=0.4.2,<0.5.0) ; extra == "groq" or extra == "test-docs"
|
|
33
|
+
Requires-Dist: jiter (>=0.4.1,<0.5.0)
|
|
34
|
+
Requires-Dist: jsonref (>=1.1.0,<2.0.0) ; extra == "vertexai"
|
|
31
35
|
Requires-Dist: litellm (>=1.35.31,<2.0.0) ; extra == "test-docs" or extra == "litellm"
|
|
32
36
|
Requires-Dist: mistralai (>=0.1.8,<0.2.0) ; extra == "test-docs" or extra == "mistralai"
|
|
33
37
|
Requires-Dist: openai (>=1.1.0,<2.0.0)
|
|
@@ -52,6 +56,10 @@ Instructor is a Python library that makes it a breeze to work with structured ou
|
|
|
52
56
|
[](https://discord.gg/bD9YE9JArw)
|
|
53
57
|
[](https://pypi.python.org/pypi/instructor)
|
|
54
58
|
|
|
59
|
+
## Want your logo on our website?
|
|
60
|
+
|
|
61
|
+
If your company use instructor a lot, we'd love to have your logo on our website! Please fill out [this form](https://q7gjsgfstrp.typeform.com/to/wluQlVVQ)
|
|
62
|
+
|
|
55
63
|
## Key Features
|
|
56
64
|
|
|
57
65
|
- **Response Models**: Specify Pydantic models to define the structure of your LLM outputs
|
|
@@ -184,20 +192,53 @@ import instructor
|
|
|
184
192
|
import google.generativeai as genai
|
|
185
193
|
from pydantic import BaseModel
|
|
186
194
|
|
|
195
|
+
|
|
187
196
|
class User(BaseModel):
|
|
188
197
|
name: str
|
|
189
198
|
age: int
|
|
190
199
|
|
|
200
|
+
|
|
191
201
|
# genai.configure(api_key=os.environ["API_KEY"]) # alternative API key configuration
|
|
192
202
|
client = instructor.from_gemini(
|
|
193
203
|
client=genai.GenerativeModel(
|
|
194
|
-
model_name="models/gemini-1.5-flash-latest",
|
|
204
|
+
model_name="models/gemini-1.5-flash-latest", # model defaults to "gemini-pro"
|
|
195
205
|
),
|
|
196
206
|
mode=instructor.Mode.GEMINI_JSON,
|
|
197
207
|
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
Alternatively, you can [call Gemini from the OpenAI client](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-gemini-using-openai-library#python).You'll have to setup [`gcloud`](https://cloud.google.com/docs/authentication/provide-credentials-adc#local-dev), get setup on Vertex AI, and install the Google Auth library.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
pip install google-auth
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
import google.auth
|
|
219
|
+
import google.auth.transport.requests
|
|
220
|
+
import instructor
|
|
221
|
+
from openai import OpenAI
|
|
222
|
+
from pydantic import BaseModel
|
|
223
|
+
|
|
224
|
+
creds, project = google.auth.default()
|
|
225
|
+
auth_req = google.auth.transport.requests.Request()
|
|
226
|
+
creds.refresh(auth_req)
|
|
227
|
+
|
|
228
|
+
# Pass the Vertex endpoint and authentication to the OpenAI SDK
|
|
229
|
+
PROJECT = 'PROJECT_ID'
|
|
230
|
+
LOCATION = 'LOCATION' # https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations
|
|
231
|
+
base_url = f'https://{LOCATION}-aiplatform.googleapis.com/v1beta1/projects/{PROJECT}/locations/{LOCATION}/endpoints/openapi'
|
|
232
|
+
|
|
233
|
+
client = instructor.from_openai(OpenAI(base_url=base_url, api_key=creds.token), mode=instructor.Mode.JSON)
|
|
234
|
+
# JSON mode is req'd
|
|
235
|
+
class User(BaseModel):
|
|
236
|
+
name: str
|
|
237
|
+
age: int
|
|
198
238
|
|
|
199
|
-
# note that client.chat.completions.create will also work
|
|
200
239
|
resp = client.chat.completions.create(
|
|
240
|
+
model="google/gemini-1.5-flash-001",
|
|
241
|
+
max_tokens=1024,
|
|
201
242
|
messages=[
|
|
202
243
|
{
|
|
203
244
|
"role": "user",
|
|
@@ -212,6 +253,7 @@ assert resp.name == "Jason"
|
|
|
212
253
|
assert resp.age == 25
|
|
213
254
|
```
|
|
214
255
|
|
|
256
|
+
|
|
215
257
|
### Using Litellm
|
|
216
258
|
|
|
217
259
|
```python
|
|
@@ -369,13 +411,13 @@ for user in user_stream:
|
|
|
369
411
|
#> name=None age=None
|
|
370
412
|
#> name=None age=None
|
|
371
413
|
#> name=None age=None
|
|
372
|
-
#> name=None age=
|
|
373
|
-
#> name=None age=
|
|
374
|
-
#> name=
|
|
375
|
-
#> name=
|
|
376
|
-
#> name=
|
|
377
|
-
#> name=
|
|
378
|
-
#> name='John Doe' age=
|
|
414
|
+
#> name=None age=None
|
|
415
|
+
#> name=None age=None
|
|
416
|
+
#> name='John Doe' age=None
|
|
417
|
+
#> name='John Doe' age=None
|
|
418
|
+
#> name='John Doe' age=None
|
|
419
|
+
#> name='John Doe' age=30
|
|
420
|
+
#> name='John Doe' age=30
|
|
379
421
|
# name=None age=None
|
|
380
422
|
# name='' age=None
|
|
381
423
|
# name='John' age=None
|
|
@@ -415,8 +457,8 @@ users = client.chat.completions.create_iterable(
|
|
|
415
457
|
|
|
416
458
|
for user in users:
|
|
417
459
|
print(user)
|
|
418
|
-
#> name='John' age=30
|
|
419
|
-
#> name='Jane' age=
|
|
460
|
+
#> name='John Doe' age=30
|
|
461
|
+
#> name='Jane Doe' age=28
|
|
420
462
|
# User(name='John Doe', age=30)
|
|
421
463
|
# User(name='Jane Smith', age=25)
|
|
422
464
|
```
|
|
@@ -6,6 +6,10 @@ Instructor is a Python library that makes it a breeze to work with structured ou
|
|
|
6
6
|
[](https://discord.gg/bD9YE9JArw)
|
|
7
7
|
[](https://pypi.python.org/pypi/instructor)
|
|
8
8
|
|
|
9
|
+
## Want your logo on our website?
|
|
10
|
+
|
|
11
|
+
If your company use instructor a lot, we'd love to have your logo on our website! Please fill out [this form](https://q7gjsgfstrp.typeform.com/to/wluQlVVQ)
|
|
12
|
+
|
|
9
13
|
## Key Features
|
|
10
14
|
|
|
11
15
|
- **Response Models**: Specify Pydantic models to define the structure of your LLM outputs
|
|
@@ -138,20 +142,53 @@ import instructor
|
|
|
138
142
|
import google.generativeai as genai
|
|
139
143
|
from pydantic import BaseModel
|
|
140
144
|
|
|
145
|
+
|
|
141
146
|
class User(BaseModel):
|
|
142
147
|
name: str
|
|
143
148
|
age: int
|
|
144
149
|
|
|
150
|
+
|
|
145
151
|
# genai.configure(api_key=os.environ["API_KEY"]) # alternative API key configuration
|
|
146
152
|
client = instructor.from_gemini(
|
|
147
153
|
client=genai.GenerativeModel(
|
|
148
|
-
model_name="models/gemini-1.5-flash-latest",
|
|
154
|
+
model_name="models/gemini-1.5-flash-latest", # model defaults to "gemini-pro"
|
|
149
155
|
),
|
|
150
156
|
mode=instructor.Mode.GEMINI_JSON,
|
|
151
157
|
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
Alternatively, you can [call Gemini from the OpenAI client](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-gemini-using-openai-library#python).You'll have to setup [`gcloud`](https://cloud.google.com/docs/authentication/provide-credentials-adc#local-dev), get setup on Vertex AI, and install the Google Auth library.
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
pip install google-auth
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
import google.auth
|
|
169
|
+
import google.auth.transport.requests
|
|
170
|
+
import instructor
|
|
171
|
+
from openai import OpenAI
|
|
172
|
+
from pydantic import BaseModel
|
|
173
|
+
|
|
174
|
+
creds, project = google.auth.default()
|
|
175
|
+
auth_req = google.auth.transport.requests.Request()
|
|
176
|
+
creds.refresh(auth_req)
|
|
177
|
+
|
|
178
|
+
# Pass the Vertex endpoint and authentication to the OpenAI SDK
|
|
179
|
+
PROJECT = 'PROJECT_ID'
|
|
180
|
+
LOCATION = 'LOCATION' # https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations
|
|
181
|
+
base_url = f'https://{LOCATION}-aiplatform.googleapis.com/v1beta1/projects/{PROJECT}/locations/{LOCATION}/endpoints/openapi'
|
|
182
|
+
|
|
183
|
+
client = instructor.from_openai(OpenAI(base_url=base_url, api_key=creds.token), mode=instructor.Mode.JSON)
|
|
184
|
+
# JSON mode is req'd
|
|
185
|
+
class User(BaseModel):
|
|
186
|
+
name: str
|
|
187
|
+
age: int
|
|
152
188
|
|
|
153
|
-
# note that client.chat.completions.create will also work
|
|
154
189
|
resp = client.chat.completions.create(
|
|
190
|
+
model="google/gemini-1.5-flash-001",
|
|
191
|
+
max_tokens=1024,
|
|
155
192
|
messages=[
|
|
156
193
|
{
|
|
157
194
|
"role": "user",
|
|
@@ -166,6 +203,7 @@ assert resp.name == "Jason"
|
|
|
166
203
|
assert resp.age == 25
|
|
167
204
|
```
|
|
168
205
|
|
|
206
|
+
|
|
169
207
|
### Using Litellm
|
|
170
208
|
|
|
171
209
|
```python
|
|
@@ -323,13 +361,13 @@ for user in user_stream:
|
|
|
323
361
|
#> name=None age=None
|
|
324
362
|
#> name=None age=None
|
|
325
363
|
#> name=None age=None
|
|
326
|
-
#> name=None age=
|
|
327
|
-
#> name=None age=
|
|
328
|
-
#> name=
|
|
329
|
-
#> name=
|
|
330
|
-
#> name=
|
|
331
|
-
#> name=
|
|
332
|
-
#> name='John Doe' age=
|
|
364
|
+
#> name=None age=None
|
|
365
|
+
#> name=None age=None
|
|
366
|
+
#> name='John Doe' age=None
|
|
367
|
+
#> name='John Doe' age=None
|
|
368
|
+
#> name='John Doe' age=None
|
|
369
|
+
#> name='John Doe' age=30
|
|
370
|
+
#> name='John Doe' age=30
|
|
333
371
|
# name=None age=None
|
|
334
372
|
# name='' age=None
|
|
335
373
|
# name='John' age=None
|
|
@@ -369,8 +407,8 @@ users = client.chat.completions.create_iterable(
|
|
|
369
407
|
|
|
370
408
|
for user in users:
|
|
371
409
|
print(user)
|
|
372
|
-
#> name='John' age=30
|
|
373
|
-
#> name='Jane' age=
|
|
410
|
+
#> name='John Doe' age=30
|
|
411
|
+
#> name='Jane Doe' age=28
|
|
374
412
|
# User(name='John Doe', age=30)
|
|
375
413
|
# User(name='Jane Smith', age=25)
|
|
376
414
|
```
|
|
@@ -74,3 +74,8 @@ if importlib.util.find_spec("cohere") is not None:
|
|
|
74
74
|
from .client_cohere import from_cohere
|
|
75
75
|
|
|
76
76
|
__all__ += ["from_cohere"]
|
|
77
|
+
|
|
78
|
+
if importlib.util.find_spec("vertexai") is not None:
|
|
79
|
+
from .client_vertexai import from_vertexai
|
|
80
|
+
|
|
81
|
+
__all__ += ["from_vertexai"]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Literal, Any, Union
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from instructor.process_response import handle_response_model
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
openai_models = Literal[
|
|
7
|
+
"gpt-4o",
|
|
8
|
+
"gpt-4-turbo",
|
|
9
|
+
"gpt-4",
|
|
10
|
+
"gpt-4-32k",
|
|
11
|
+
"gpt-3.5-turbo",
|
|
12
|
+
"gpt-3.5-turbo-16k",
|
|
13
|
+
"gpt-4-turbo-preview",
|
|
14
|
+
"gpt-4-vision-preview",
|
|
15
|
+
"gpt-4-turbo-2024-04-09",
|
|
16
|
+
"gpt-4-0314",
|
|
17
|
+
"gpt-4-32k-0314",
|
|
18
|
+
"gpt-4-32k-0613",
|
|
19
|
+
"gpt-3.5-turbo-0301",
|
|
20
|
+
"gpt-3.5-turbo-16k-0613",
|
|
21
|
+
"gpt-3.5-turbo-1106",
|
|
22
|
+
"gpt-3.5-turbo-0613",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Function(BaseModel):
|
|
27
|
+
name: str
|
|
28
|
+
description: str
|
|
29
|
+
parameters: Any
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Tool(BaseModel):
|
|
33
|
+
type: str
|
|
34
|
+
function: Function
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RequestBody(BaseModel):
|
|
38
|
+
model: Union[openai_models, str]
|
|
39
|
+
messages: list[dict[str, Any]]
|
|
40
|
+
max_tokens: int = Field(default=1000)
|
|
41
|
+
tools: list[Tool]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BatchModel(BaseModel):
|
|
45
|
+
custom_id: str
|
|
46
|
+
method: Literal["POST"]
|
|
47
|
+
url: Literal["/v1/chat/completions"]
|
|
48
|
+
body: RequestBody
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class BatchJob:
|
|
52
|
+
@classmethod
|
|
53
|
+
def create_from_messages(
|
|
54
|
+
cls,
|
|
55
|
+
messages_batch: list[list[dict[str, Any]]],
|
|
56
|
+
model: Union[openai_models, str],
|
|
57
|
+
response_model: type[BaseModel],
|
|
58
|
+
max_tokens: int = 1000,
|
|
59
|
+
):
|
|
60
|
+
_, tools = handle_response_model(response_model=response_model)
|
|
61
|
+
return [
|
|
62
|
+
BatchModel(
|
|
63
|
+
custom_id=str(uuid.uuid4()),
|
|
64
|
+
method="POST",
|
|
65
|
+
url="/v1/chat/completions",
|
|
66
|
+
body=RequestBody(
|
|
67
|
+
model=model,
|
|
68
|
+
max_tokens=max_tokens,
|
|
69
|
+
messages=messages,
|
|
70
|
+
**tools,
|
|
71
|
+
),
|
|
72
|
+
).model_dump(mode="json")
|
|
73
|
+
for messages in messages_batch
|
|
74
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
from rich.live import Live
|
|
4
|
+
from openai import OpenAI
|
|
5
|
+
from openai.types.batch import Batch
|
|
6
|
+
import typer
|
|
7
|
+
import datetime
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
client = OpenAI()
|
|
11
|
+
app = typer.Typer()
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_table(batch_jobs: list[Batch]):
|
|
17
|
+
table = Table(
|
|
18
|
+
title="OpenAI Batch Jobs",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
table.add_column("Batch ID", style="dim")
|
|
22
|
+
table.add_column("Created At")
|
|
23
|
+
table.add_column("Status")
|
|
24
|
+
table.add_column("Failed")
|
|
25
|
+
table.add_column("Completed")
|
|
26
|
+
table.add_column("Total")
|
|
27
|
+
|
|
28
|
+
for batch_job in batch_jobs:
|
|
29
|
+
table.add_row(
|
|
30
|
+
batch_job.id,
|
|
31
|
+
str(datetime.datetime.fromtimestamp(batch_job.created_at)),
|
|
32
|
+
batch_job.status,
|
|
33
|
+
str(batch_job.request_counts.failed), # type: ignore
|
|
34
|
+
str(batch_job.request_counts.completed), # type: ignore
|
|
35
|
+
str(batch_job.request_counts.total), # type: ignore
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return table
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_jobs(limit: int = 10):
|
|
42
|
+
return client.batches.list(limit=limit).data
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.command(name="list", help="See all existing batch jobs")
|
|
46
|
+
def watch(
|
|
47
|
+
limit: int = typer.Option(10, help="Total number of batch jobs to show"),
|
|
48
|
+
poll: int = typer.Option(
|
|
49
|
+
10, help="Time in seconds to wait for the batch job to complete"
|
|
50
|
+
),
|
|
51
|
+
screen: bool = typer.Option(False, help="Enable or disable screen output"),
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Monitor the status of the most recent batch jobs
|
|
55
|
+
"""
|
|
56
|
+
batch_jobs = get_jobs(limit)
|
|
57
|
+
table = generate_table(batch_jobs)
|
|
58
|
+
with Live(
|
|
59
|
+
generate_table(batch_jobs), refresh_per_second=2, screen=screen
|
|
60
|
+
) as live_table:
|
|
61
|
+
while True:
|
|
62
|
+
batch_jobs = get_jobs(limit)
|
|
63
|
+
table = generate_table(batch_jobs)
|
|
64
|
+
live_table.update(table)
|
|
65
|
+
time.sleep(poll)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command(
|
|
69
|
+
help="Create a batch job from a file",
|
|
70
|
+
)
|
|
71
|
+
def create_from_file(
|
|
72
|
+
file_path: str = typer.Option(help="File containing the batch job requests"),
|
|
73
|
+
):
|
|
74
|
+
with console.status(f"[bold green] Uploading batch job file...", spinner="dots"):
|
|
75
|
+
batch_input_file = client.files.create(
|
|
76
|
+
file=open(file_path, "rb"), purpose="batch"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
batch_input_file_id = batch_input_file.id
|
|
80
|
+
|
|
81
|
+
with console.status(
|
|
82
|
+
f"[bold green] Creating batch job from ID {batch_input_file_id}", spinner="dots"
|
|
83
|
+
):
|
|
84
|
+
client.batches.create(
|
|
85
|
+
input_file_id=batch_input_file_id,
|
|
86
|
+
endpoint="/v1/chat/completions",
|
|
87
|
+
completion_window="24h",
|
|
88
|
+
metadata={"description": "testing job"},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
watch(limit=5, poll=2, screen=False)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@app.command(help="Cancel a batch job")
|
|
95
|
+
def cancel(batch_id: str = typer.Option(help="Batch job ID to cancel")):
|
|
96
|
+
try:
|
|
97
|
+
client.batches.cancel(batch_id)
|
|
98
|
+
watch(limit=5, poll=2, screen=False)
|
|
99
|
+
console.log(f"[bold red]Job {batch_id} cancelled successfully!")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
console.log(f"[bold red]Error cancelling job {batch_id}: {e}")
|
|
@@ -3,6 +3,7 @@ import instructor.cli.jobs as jobs
|
|
|
3
3
|
import instructor.cli.files as files
|
|
4
4
|
import instructor.cli.usage as usage
|
|
5
5
|
import instructor.cli.hub as hub
|
|
6
|
+
import instructor.cli.batch as batch
|
|
6
7
|
|
|
7
8
|
app = typer.Typer()
|
|
8
9
|
|
|
@@ -10,6 +11,7 @@ app.add_typer(jobs.app, name="jobs", help="Monitor and create fine tuning jobs")
|
|
|
10
11
|
app.add_typer(files.app, name="files", help="Manage files on OpenAI's servers")
|
|
11
12
|
app.add_typer(usage.app, name="usage", help="Check OpenAI API usage data")
|
|
12
13
|
app.add_typer(hub.app, name="hub", help="Interact with the instructor hub")
|
|
14
|
+
app.add_typer(batch.app, name="batch", help="Manage OpenAI Batch jobs")
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
@app.command()
|
|
@@ -18,6 +20,6 @@ def docs(query: str = typer.Argument(None, help="Search the documentation")) ->
|
|
|
18
20
|
Open the instructor documentation website.
|
|
19
21
|
"""
|
|
20
22
|
if query:
|
|
21
|
-
typer.launch(f"https://
|
|
23
|
+
typer.launch(f"https://python.useinstructor.com/?q={query}")
|
|
22
24
|
else:
|
|
23
|
-
typer.launch("https://
|
|
25
|
+
typer.launch("https://python.useinstructor.com/")
|
|
@@ -118,11 +118,11 @@ def calculate_cost(
|
|
|
118
118
|
|
|
119
119
|
def group_and_sum_by_date_and_snapshot(usage_data: list[dict[str, Any]]) -> Table:
|
|
120
120
|
"""Group and sum the usage data by date and snapshot, including costs."""
|
|
121
|
-
summary: defaultdict[
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
summary: defaultdict[str, defaultdict[str, dict[str, Union[int, float]]]] = (
|
|
122
|
+
defaultdict(
|
|
123
|
+
lambda: defaultdict(
|
|
124
|
+
lambda: {"total_requests": 0, "total_tokens": 0, "total_cost": 0.0}
|
|
125
|
+
)
|
|
126
126
|
)
|
|
127
127
|
)
|
|
128
128
|
|
|
@@ -63,8 +63,7 @@ class Instructor:
|
|
|
63
63
|
validation_context: dict[str, Any] | None = None,
|
|
64
64
|
strict: bool = True,
|
|
65
65
|
**kwargs: Any,
|
|
66
|
-
) -> Awaitable[T]:
|
|
67
|
-
...
|
|
66
|
+
) -> Awaitable[T]: ...
|
|
68
67
|
|
|
69
68
|
@overload
|
|
70
69
|
def create(
|
|
@@ -75,8 +74,7 @@ class Instructor:
|
|
|
75
74
|
validation_context: dict[str, Any] | None = None,
|
|
76
75
|
strict: bool = True,
|
|
77
76
|
**kwargs: Any,
|
|
78
|
-
) -> T:
|
|
79
|
-
...
|
|
77
|
+
) -> T: ...
|
|
80
78
|
|
|
81
79
|
# TODO: we should overload a case where response_model is None
|
|
82
80
|
def create(
|
|
@@ -108,8 +106,7 @@ class Instructor:
|
|
|
108
106
|
validation_context: dict[str, Any] | None = None,
|
|
109
107
|
strict: bool = True,
|
|
110
108
|
**kwargs: Any,
|
|
111
|
-
) -> AsyncGenerator[T, None]:
|
|
112
|
-
...
|
|
109
|
+
) -> AsyncGenerator[T, None]: ...
|
|
113
110
|
|
|
114
111
|
@overload
|
|
115
112
|
def create_partial(
|
|
@@ -120,8 +117,7 @@ class Instructor:
|
|
|
120
117
|
validation_context: dict[str, Any] | None = None,
|
|
121
118
|
strict: bool = True,
|
|
122
119
|
**kwargs: Any,
|
|
123
|
-
) -> Generator[T, None, None]:
|
|
124
|
-
...
|
|
120
|
+
) -> Generator[T, None, None]: ...
|
|
125
121
|
|
|
126
122
|
def create_partial(
|
|
127
123
|
self,
|
|
@@ -155,8 +151,7 @@ class Instructor:
|
|
|
155
151
|
validation_context: dict[str, Any] | None = None,
|
|
156
152
|
strict: bool = True,
|
|
157
153
|
**kwargs: Any,
|
|
158
|
-
) -> AsyncGenerator[T, None]:
|
|
159
|
-
...
|
|
154
|
+
) -> AsyncGenerator[T, None]: ...
|
|
160
155
|
|
|
161
156
|
@overload
|
|
162
157
|
def create_iterable(
|
|
@@ -167,8 +162,7 @@ class Instructor:
|
|
|
167
162
|
validation_context: dict[str, Any] | None = None,
|
|
168
163
|
strict: bool = True,
|
|
169
164
|
**kwargs: Any,
|
|
170
|
-
) -> Generator[T, None, None]:
|
|
171
|
-
...
|
|
165
|
+
) -> Generator[T, None, None]: ...
|
|
172
166
|
|
|
173
167
|
def create_iterable(
|
|
174
168
|
self,
|
|
@@ -179,8 +173,6 @@ class Instructor:
|
|
|
179
173
|
strict: bool = True,
|
|
180
174
|
**kwargs: Any,
|
|
181
175
|
) -> Generator[T, None, None] | AsyncGenerator[T, None]:
|
|
182
|
-
assert self.provider != Provider.ANTHROPIC, "Anthropic doesn't support iterable"
|
|
183
|
-
|
|
184
176
|
kwargs["stream"] = True
|
|
185
177
|
kwargs = self.handle_kwargs(kwargs)
|
|
186
178
|
|
|
@@ -203,8 +195,7 @@ class Instructor:
|
|
|
203
195
|
validation_context: dict[str, Any] | None = None,
|
|
204
196
|
strict: bool = True,
|
|
205
197
|
**kwargs: Any,
|
|
206
|
-
) -> Awaitable[tuple[T, Any]]:
|
|
207
|
-
...
|
|
198
|
+
) -> Awaitable[tuple[T, Any]]: ...
|
|
208
199
|
|
|
209
200
|
@overload
|
|
210
201
|
def create_with_completion(
|
|
@@ -215,8 +206,7 @@ class Instructor:
|
|
|
215
206
|
validation_context: dict[str, Any] | None = None,
|
|
216
207
|
strict: bool = True,
|
|
217
208
|
**kwargs: Any,
|
|
218
|
-
) -> tuple[T, Any]:
|
|
219
|
-
...
|
|
209
|
+
) -> tuple[T, Any]: ...
|
|
220
210
|
|
|
221
211
|
def create_with_completion(
|
|
222
212
|
self,
|
|
@@ -432,8 +422,7 @@ def from_litellm(
|
|
|
432
422
|
completion: Callable[..., Any],
|
|
433
423
|
mode: instructor.Mode = instructor.Mode.TOOLS,
|
|
434
424
|
**kwargs: Any,
|
|
435
|
-
) -> Instructor:
|
|
436
|
-
...
|
|
425
|
+
) -> Instructor: ...
|
|
437
426
|
|
|
438
427
|
|
|
439
428
|
@overload
|
|
@@ -11,10 +11,9 @@ def from_anthropic(
|
|
|
11
11
|
client: (
|
|
12
12
|
anthropic.Anthropic | anthropic.AnthropicBedrock | anthropic.AnthropicVertex
|
|
13
13
|
),
|
|
14
|
-
mode: instructor.Mode = instructor.Mode.
|
|
14
|
+
mode: instructor.Mode = instructor.Mode.ANTHROPIC_TOOLS,
|
|
15
15
|
**kwargs: Any,
|
|
16
|
-
) -> instructor.Instructor:
|
|
17
|
-
...
|
|
16
|
+
) -> instructor.Instructor: ...
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
@overload
|
|
@@ -24,10 +23,9 @@ def from_anthropic(
|
|
|
24
23
|
| anthropic.AsyncAnthropicBedrock
|
|
25
24
|
| anthropic.AsyncAnthropicVertex
|
|
26
25
|
),
|
|
27
|
-
mode: instructor.Mode = instructor.Mode.
|
|
26
|
+
mode: instructor.Mode = instructor.Mode.ANTHROPIC_TOOLS,
|
|
28
27
|
**kwargs: Any,
|
|
29
|
-
) -> instructor.AsyncInstructor:
|
|
30
|
-
...
|
|
28
|
+
) -> instructor.AsyncInstructor: ...
|
|
31
29
|
|
|
32
30
|
|
|
33
31
|
def from_anthropic(
|
|
@@ -39,7 +37,7 @@ def from_anthropic(
|
|
|
39
37
|
| anthropic.AsyncAnthropicVertex
|
|
40
38
|
| anthropic.AnthropicVertex
|
|
41
39
|
),
|
|
42
|
-
mode: instructor.Mode = instructor.Mode.
|
|
40
|
+
mode: instructor.Mode = instructor.Mode.ANTHROPIC_TOOLS,
|
|
43
41
|
**kwargs: Any,
|
|
44
42
|
) -> instructor.Instructor | instructor.AsyncInstructor:
|
|
45
43
|
assert (
|
|
@@ -62,10 +60,7 @@ def from_anthropic(
|
|
|
62
60
|
),
|
|
63
61
|
), "Client must be an instance of {anthropic.Anthropic, anthropic.AsyncAnthropic, anthropic.AnthropicBedrock, anthropic.AsyncAnthropicBedrock, anthropic.AnthropicVertex, anthropic.AsyncAnthropicVertex}"
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
create = client.beta.tools.messages.create # type: ignore - unknown in stubs
|
|
67
|
-
else:
|
|
68
|
-
create = client.messages.create
|
|
63
|
+
create = client.messages.create
|
|
69
64
|
|
|
70
65
|
if isinstance(
|
|
71
66
|
client,
|
|
@@ -23,8 +23,7 @@ def from_cohere(
|
|
|
23
23
|
client: cohere.Client,
|
|
24
24
|
mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
|
|
25
25
|
**kwargs: Any,
|
|
26
|
-
) -> instructor.Instructor:
|
|
27
|
-
...
|
|
26
|
+
) -> instructor.Instructor: ...
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
@overload
|
|
@@ -32,8 +31,7 @@ def from_cohere(
|
|
|
32
31
|
client: cohere.AsyncClient,
|
|
33
32
|
mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
|
|
34
33
|
**kwargs: Any,
|
|
35
|
-
) -> instructor.AsyncInstructor:
|
|
36
|
-
...
|
|
34
|
+
) -> instructor.AsyncInstructor: ...
|
|
37
35
|
|
|
38
36
|
|
|
39
37
|
def from_cohere(
|