mirror of
https://github.com/kennethreitz/instructor.git
synced 2026-06-05 14:50:16 +00:00
Test all of our documentation. (#404)
Co-authored-by: grit-app[bot] <grit-app[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
name: Test Docs
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.11']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Cache Poetry virtualenv
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pypoetry/virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1.3.1
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install --with dev
|
||||
|
||||
- name: Install doc dependencies
|
||||
run: poetry install --with test-docs
|
||||
|
||||
- name: Run tests
|
||||
run: poetry run pytest tests/openai/docs
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
@@ -169,3 +169,4 @@ tutorials/results.jsonl
|
||||
tutorials/results.jsonlines
|
||||
tutorials/schema.json
|
||||
wandb/settings
|
||||
math_finetunes.jsonl
|
||||
|
||||
@@ -2,5 +2,3 @@ version: 0.0.1
|
||||
patterns:
|
||||
- name: github.com/getgrit/python#openai
|
||||
level: info
|
||||
- name: github.com/getgrit/python#no_skipped_tests
|
||||
level: error
|
||||
|
||||
@@ -79,10 +79,11 @@ For async clients you must use `apatch` vs. `patch`, as shown:
|
||||
|
||||
```py
|
||||
import instructor
|
||||
from openai import AsyncOpenAI
|
||||
import asyncio
|
||||
import openai
|
||||
from pydantic import BaseModel
|
||||
|
||||
aclient = instructor.apatch(AsyncOpenAI())
|
||||
aclient = instructor.apatch(openai.AsyncOpenAI())
|
||||
|
||||
|
||||
class UserExtract(BaseModel):
|
||||
@@ -90,7 +91,7 @@ class UserExtract(BaseModel):
|
||||
age: int
|
||||
|
||||
|
||||
model = await aclient.chat.completions.create(
|
||||
task = aclient.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserExtract,
|
||||
messages=[
|
||||
@@ -98,7 +99,15 @@ model = await aclient.chat.completions.create(
|
||||
],
|
||||
)
|
||||
|
||||
assert isinstance(model, UserExtract)
|
||||
|
||||
response = asyncio.run(task)
|
||||
print(response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "Jason",
|
||||
"age": 25
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
### Step 1: Patch the client
|
||||
@@ -132,8 +141,19 @@ class UserDetail(BaseModel):
|
||||
Use the `client.chat.completions.create` method to send a prompt and extract the data into the Pydantic object. The `response_model` parameter specifies the Pydantic model to use for extraction. It is helpful to annotate the variable with the type of the response model which will help your IDE provide autocomplete and spell check.
|
||||
|
||||
```python
|
||||
import instructor
|
||||
import openai
|
||||
from pydantic import BaseModel
|
||||
|
||||
user: UserDetail = client.chat.completions.create(
|
||||
client = instructor.patch(openai.OpenAI())
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
user = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserDetail,
|
||||
messages=[
|
||||
@@ -141,8 +161,16 @@ user: UserDetail = client.chat.completions.create(
|
||||
],
|
||||
)
|
||||
|
||||
assert isinstance(user, UserDetail)
|
||||
assert user.name == "Jason"
|
||||
assert user.age == 25
|
||||
print(user.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "Jason",
|
||||
"age": 25
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
## Pydantic Validation
|
||||
|
||||
@@ -70,7 +70,7 @@ resp = client.chat.completions.create(
|
||||
response_model=UserDetails,
|
||||
)
|
||||
print(resp)
|
||||
# >>> name='Jason' age=20
|
||||
# # > name='Jason' age=20
|
||||
```
|
||||
|
||||
You can find more information about Anyscale's output mode support [here](https://docs.endpoints.anyscale.com/).
|
||||
|
||||
@@ -76,16 +76,16 @@ Now we can call `extract` multiple times with the same argument, and the result
|
||||
```python hl_lines="4 8 12"
|
||||
import time
|
||||
|
||||
start = time.perf_counter() # (1)
|
||||
start = time.perf_counter() # (1)
|
||||
model = extract("Extract jason is 25 years old")
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
|
||||
start = time.perf_counter()
|
||||
model = extract("Extract jason is 25 years old") # (2)
|
||||
model = extract("Extract jason is 25 years old") # (2)
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
|
||||
>>> Time taken: 0.9267581660533324
|
||||
>>> Time taken: 1.2080417945981026e-06 # (3)
|
||||
#> Time taken: 0.92
|
||||
#> Time taken: 1.20e-06 # (3)
|
||||
```
|
||||
|
||||
1. Using `time.perf_counter()` to measure the time taken to run the function is better than using `time.time()` because it's more accurate and less susceptible to system clock changes.
|
||||
@@ -101,20 +101,23 @@ print(f"Time taken: {time.perf_counter() - start}")
|
||||
```python hl_lines="3-5 9"
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
print("Do something before") # (1)
|
||||
print("Do something before") # (1)
|
||||
result = func(*args, **kwargs)
|
||||
print("Do something after") # (2)
|
||||
print("Do something after") # (2)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@decorator
|
||||
def say_hello():
|
||||
print("Hello!")
|
||||
|
||||
|
||||
say_hello()
|
||||
>>> "Do something before"
|
||||
>>> "Hello!"
|
||||
>>> "Do something after"
|
||||
#> "Do something before"
|
||||
#> "Hello!"
|
||||
#> "Do something after"
|
||||
```
|
||||
|
||||
1. The code is executed before the function is called
|
||||
|
||||
@@ -115,21 +115,21 @@ Firstly, we'll need a data model for the initial summary that we will be generat
|
||||
|
||||
```py
|
||||
class GeneratedSummary(BaseModel):
|
||||
"""
|
||||
This represents a highly concise summary that includes as many entities as possible from the original source article.
|
||||
"""
|
||||
This represents a highly concise summary that includes as many entities as possible from the original source article.
|
||||
|
||||
An Entity is a real-world object that's assigned a name - for example, a person, country a product or a book title.
|
||||
An Entity is a real-world object that's assigned a name - for example, a person, country a product or a book title.
|
||||
|
||||
Guidelines
|
||||
- Make every word count
|
||||
- The new summary should be highly dense and concise yet self-contained, eg., easily understood without the Article.
|
||||
- Make space with fusion, compression, and removal of uninformative phrases like "the article discusses"
|
||||
"""
|
||||
Guidelines
|
||||
- Make every word count
|
||||
- The new summary should be highly dense and concise yet self-contained, eg., easily understood without the Article.
|
||||
- Make space with fusion, compression, and removal of uninformative phrases like "the article discusses"
|
||||
"""
|
||||
|
||||
summary: str = Field(
|
||||
...,
|
||||
description="This represents the final summary generated that captures the meaning of the original article which is as concise as possible. ",
|
||||
)
|
||||
summary: str = Field(
|
||||
...,
|
||||
description="This represents the final summary generated that captures the meaning of the original article which is as concise as possible. ",
|
||||
)
|
||||
```
|
||||
|
||||
We eventually transform it into an OpenAI function call as seen below.
|
||||
@@ -254,21 +254,21 @@ def has_no_absent_entities(cls, absent_entities: List[str]):
|
||||
return absent_entities
|
||||
|
||||
@field_validator("summary")
|
||||
def min_entity_density(cls, v: str):
|
||||
tokens = nltk.word_tokenize(v)
|
||||
num_tokens = len(tokens)
|
||||
def min_entity_density(cls, v: str):
|
||||
tokens = nltk.word_tokenize(v)
|
||||
num_tokens = len(tokens)
|
||||
|
||||
# Extract Entities
|
||||
doc = nlp(v) #(2)!
|
||||
num_entities = len(doc.ents)
|
||||
# Extract Entities
|
||||
doc = nlp(v) #(2)!
|
||||
num_entities = len(doc.ents)
|
||||
|
||||
density = num_entities / num_tokens
|
||||
if density < 0.08: #(3)!
|
||||
raise ValueError(
|
||||
f"The summary of {v} has too few entities. Please regenerate a new summary with more new entities added to it. Remember that new entities can be added at any point of the summary."
|
||||
)
|
||||
density = num_entities / num_tokens
|
||||
if density < 0.08: #(3)!
|
||||
raise ValueError(
|
||||
f"The summary of {v} has too few entities. Please regenerate a new summary with more new entities added to it. Remember that new entities can be added at any point of the summary."
|
||||
)
|
||||
|
||||
return v
|
||||
return v
|
||||
```
|
||||
|
||||
1. Similar to the original paper, we utilize the `NLTK` word tokenizer to count the number of tokens within our generated sentences.
|
||||
@@ -282,7 +282,7 @@ def has_no_absent_entities(cls, absent_entities: List[str]):
|
||||
|
||||
Now that we have our models and the rough flow figured out, let's implement a function to summarize a piece of text using `Chain Of Density` summarization.
|
||||
|
||||
```py hl_lines="4 9-24 38-68"
|
||||
```python hl_lines="4 9-24 38-68"
|
||||
from openai import OpenAI
|
||||
import instructor
|
||||
|
||||
|
||||
@@ -163,11 +163,18 @@ The architecture resembles FastAPI. Most code can be written as Python functions
|
||||
### FastAPI Stub
|
||||
|
||||
```python
|
||||
app = FastAPI()
|
||||
import fastapi
|
||||
from pydantic import BaseModel
|
||||
|
||||
class UserDetails(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
app = fastapi.FastAPI()
|
||||
|
||||
@app.get("/user/{user_id}", response_model=UserDetails)
|
||||
async def get_user(user_id: int) -> UserDetails:
|
||||
return UserDetails(...)
|
||||
return ...
|
||||
```
|
||||
|
||||
### Using Instructor as a Function
|
||||
@@ -176,7 +183,7 @@ async def get_user(user_id: int) -> UserDetails:
|
||||
def extract_user(str) -> UserDetails:
|
||||
return client.chat.completions(
|
||||
response_model=UserDetails,
|
||||
messages=[...]
|
||||
messages=[]
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -34,12 +34,13 @@ def validation_function(value):
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import instructor # pip install instructor
|
||||
import instructor # pip install instructor
|
||||
from pydantic import BaseModel
|
||||
|
||||
# This enables response_model keyword
|
||||
# from client.chat.completions.create
|
||||
client = instructor.patch(OpenAI()) # (1)!
|
||||
client = instructor.patch(OpenAI()) # (1)!
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
@@ -51,11 +52,11 @@ user: UserDetail = client.chat.completions.create(
|
||||
response_model=UserDetail,
|
||||
messages=[
|
||||
{"role": "user", "content": "Extract Jason is 25 years old"},
|
||||
]
|
||||
max_retries=3 # (2)!
|
||||
],
|
||||
max_retries=3, # (2)!
|
||||
)
|
||||
|
||||
assert user.name == "Jason" # (3)!
|
||||
assert user.name == "Jason" # (3)!
|
||||
assert user.age == 25
|
||||
```
|
||||
|
||||
|
||||
+30
-27
@@ -5,12 +5,13 @@ If you want to learn more about concepts in caching and how to use them in your
|
||||
**When to Use**: Ideal for functions with immutable arguments, called repeatedly with the same parameters in small to medium-sized applications. This makes sense when we might be reusing the same data within a single session. or in an application where we don't need to persist the cache between sessions.
|
||||
|
||||
```python
|
||||
import time
|
||||
import functools
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.patch(OpenAI())
|
||||
client = instructor.patch(openai.OpenAI())
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
@@ -27,33 +28,28 @@ def extract(data) -> UserDetail:
|
||||
{"role": "user", "content": data},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
start = time.perf_counter() # (1)
|
||||
model = extract("Extract jason is 25 years old")
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
#> Time taken: 0.6282629589550197
|
||||
|
||||
start = time.perf_counter()
|
||||
model = extract("Extract jason is 25 years old") # (2)
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
#> Time taken: 1.9171275198459625e-06
|
||||
```
|
||||
|
||||
1. Using `time.perf_counter()` to measure the time taken to run the function is better than using `time.time()` because it's more accurate and less susceptible to system clock changes.
|
||||
2. The second time we call `extract`, the result is returned from the cache, and the function is not called.
|
||||
|
||||
!!! warning "Changing the Model does not Invalidate the Cache"
|
||||
|
||||
Note that changing the model does not invalidate the cache. This is because the cache key is based on the function's name and arguments, not the model. This means that if we change the model, the cache will still return the old result.
|
||||
|
||||
Now we can call `extract` multiple times with the same argument, and the result will be cached in memory for faster access.
|
||||
|
||||
```python hl_lines="4 8 12"
|
||||
import time
|
||||
|
||||
start = time.perf_counter() # (1)
|
||||
model = extract("Extract jason is 25 years old")
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
|
||||
start = time.perf_counter()
|
||||
model = extract("Extract jason is 25 years old") # (2)
|
||||
print(f"Time taken: {time.perf_counter() - start}")
|
||||
|
||||
>>> Time taken: 0.9267581660533324
|
||||
>>> Time taken: 1.2080417945981026e-06 # (3)
|
||||
```
|
||||
|
||||
1. Using `time.perf_counter()` to measure the time taken to run the function is better than using `time.time()` because it's more accurate and less susceptible to system clock changes.
|
||||
2. The second time we call `extract`, the result is returned from the cache, and the function is not called.
|
||||
3. The second call to `extract` is much faster because the result is returned from the cache!
|
||||
|
||||
**Benefits**: Easy to implement, provides fast access due to in-memory storage, and requires no additional libraries.
|
||||
|
||||
??? question "What is a decorator?"
|
||||
@@ -63,20 +59,27 @@ print(f"Time taken: {time.perf_counter() - start}")
|
||||
```python hl_lines="3-5 9"
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
print("Do something before") # (1)
|
||||
print("Do something before") # (1)
|
||||
#> Do something before
|
||||
result = func(*args, **kwargs)
|
||||
print("Do something after") # (2)
|
||||
print("Do something after") # (2)
|
||||
#> Do something after
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@decorator
|
||||
def say_hello():
|
||||
#> Hello!
|
||||
print("Hello!")
|
||||
#> Hello!
|
||||
|
||||
|
||||
say_hello()
|
||||
>>> "Do something before"
|
||||
>>> "Hello!"
|
||||
>>> "Do something after"
|
||||
#> "Do something before"
|
||||
#> "Hello!"
|
||||
#> "Do something after"
|
||||
```
|
||||
|
||||
1. The code is executed before the function is called
|
||||
|
||||
@@ -54,19 +54,20 @@ def fn(a: int, b: int) -> Multiply:
|
||||
|
||||
# Generate some data
|
||||
for _ in range(10):
|
||||
random.seed(42)
|
||||
a = random.randint(100, 999)
|
||||
b = random.randint(100, 999)
|
||||
print(fn(a, b))
|
||||
#> a=958 b=650 result=622700
|
||||
#> a=538 b=495 result=266310
|
||||
#> a=703 b=250 result=175750
|
||||
#> a=803 b=212 result=170236
|
||||
#> a=499 b=199 result=99301
|
||||
#> a=893 b=738 result=659034
|
||||
#> a=414 b=251 result=103914
|
||||
#> a=916 b=776 result=710816
|
||||
#> a=219 b=764 result=167316
|
||||
#> a=764 b=700 result=534800
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
#> a=754 b=214 result=161356
|
||||
```
|
||||
|
||||
## The Intricacies of Fine-tuning Language Models
|
||||
@@ -117,6 +118,14 @@ Once a model is trained you can simply change `mode` to `dispatch` and it will u
|
||||
|
||||
```python
|
||||
from instructor import Instructions
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Multiply(BaseModel):
|
||||
a: int
|
||||
b: int
|
||||
result: int
|
||||
|
||||
|
||||
instructions = Instructions(
|
||||
name="three_digit_multiply",
|
||||
|
||||
+12
-3
@@ -1,23 +1,32 @@
|
||||
To prevent data misalignment, we can use Enums for standardized fields. Always include an "Other" option as a fallback so the model can signal uncertainty.
|
||||
|
||||
```python hl_lines="7 12"
|
||||
from enum import Enum, auto
|
||||
from pydantic import BaseModel, Field
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Role(Enum):
|
||||
PRINCIPAL = "PRINCIPAL"
|
||||
TEACHER = "TEACHER"
|
||||
STUDENT = "STUDENT"
|
||||
OTHER = "OTHER""
|
||||
OTHER = "OTHER"
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
role: Role = Field(description="Correctly assign one of the predefined roles to the user.")
|
||||
role: Role = Field(
|
||||
description="Correctly assign one of the predefined roles to the user."
|
||||
)
|
||||
```
|
||||
|
||||
If you're having a hard time with `Enum` and alternative is to use `Literal` instead.
|
||||
|
||||
```python hl_lines="4"
|
||||
from typing import Literal
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
|
||||
@@ -35,7 +35,7 @@ class UserDetail(BaseModel):
|
||||
|
||||
|
||||
@app.post("/endpoint", response_model=UserDetail)
|
||||
def endpoint_function(data: UserData) -> UserDetail:
|
||||
async def endpoint_function(data: UserData) -> UserDetail:
|
||||
user_detail = await client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserDetail,
|
||||
@@ -51,8 +51,21 @@ def endpoint_function(data: UserData) -> UserDetail:
|
||||
`FastAPI` supports streaming responses, which is useful for returning large amounts of data. This feature is particularly useful when working with large language models (LLMs) that generate a large amount of data.
|
||||
|
||||
```python hl_lines="6-7"
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import Iterable
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class UserData(BaseModel):
|
||||
query: str
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
# Route to handle SSE events and return users
|
||||
|
||||
+4
-15
@@ -100,12 +100,11 @@ These all work as great opportunities to add more information to the JSON schema
|
||||
Here's an example:
|
||||
|
||||
```py
|
||||
from pydantic import BaseModel, EmailStr, Field, SecretStr
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
age: int = Field(description='Age of the user')
|
||||
email: EmailStr = Field(examples=['marcelo@mail.com'])
|
||||
name: str = Field(title='Username')
|
||||
password: SecretStr = Field(
|
||||
json_schema_extra={
|
||||
@@ -120,17 +119,7 @@ print(User.model_json_schema())
|
||||
"""
|
||||
{
|
||||
'properties': {
|
||||
'age': {
|
||||
'description': 'Age of the user',
|
||||
'title': 'Age',
|
||||
'type': 'integer',
|
||||
},
|
||||
'email': {
|
||||
'examples': ['marcelo@mail.com'],
|
||||
'format': 'email',
|
||||
'title': 'Email',
|
||||
'type': 'string',
|
||||
},
|
||||
'age': {'description': 'Age of the user', 'title': 'Age', 'type': 'integer'},
|
||||
'name': {'title': 'Username', 'type': 'string'},
|
||||
'password': {
|
||||
'description': 'Password of the user',
|
||||
@@ -141,14 +130,14 @@ print(User.model_json_schema())
|
||||
'writeOnly': True,
|
||||
},
|
||||
},
|
||||
'required': ['age', 'email', 'name', 'password'],
|
||||
'required': ['age', 'name', 'password'],
|
||||
'title': 'User',
|
||||
'type': 'object',
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
## General notes on JSON schema generation
|
||||
# General notes on JSON schema generation
|
||||
|
||||
- The JSON schema for Optional fields indicates that the value null is allowed.
|
||||
- The Decimal type is exposed in JSON schema (and serialized) as a string.
|
||||
|
||||
+76
-27
@@ -3,6 +3,7 @@
|
||||
A common use case of structured extraction is defining a single schema class and then making another schema to create a list to do multiple extraction
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -13,6 +14,30 @@ class User(BaseModel):
|
||||
|
||||
class Users(BaseModel):
|
||||
users: List[User]
|
||||
|
||||
|
||||
print(Users.model_json_schema())
|
||||
"""
|
||||
{
|
||||
'$defs': {
|
||||
'User': {
|
||||
'properties': {
|
||||
'name': {'title': 'Name', 'type': 'string'},
|
||||
'age': {'title': 'Age', 'type': 'integer'},
|
||||
},
|
||||
'required': ['name', 'age'],
|
||||
'title': 'User',
|
||||
'type': 'object',
|
||||
}
|
||||
},
|
||||
'properties': {
|
||||
'users': {'items': {'$ref': '#/$defs/User'}, 'title': 'Users', 'type': 'array'}
|
||||
},
|
||||
'required': ['users'],
|
||||
'title': 'Users',
|
||||
'type': 'object',
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
Defining a task and creating a list of classes is a common enough pattern that we make this convenient by making use of `Iterable[T]`. This lets us dynamically create a new class that:
|
||||
@@ -32,16 +57,16 @@ from pydantic import BaseModel
|
||||
|
||||
client = instructor.patch(OpenAI(), mode=instructor.function_calls.Mode.JSON)
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
Users = Iterable[User]
|
||||
|
||||
users = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo-1106",
|
||||
temperature=0.1,
|
||||
response_model=Users,
|
||||
response_model=Iterable[User],
|
||||
stream=False,
|
||||
messages=[
|
||||
{
|
||||
@@ -53,11 +78,11 @@ users = client.chat.completions.create(
|
||||
],
|
||||
)
|
||||
for user in users:
|
||||
assert isinstance(user, User)
|
||||
print(user)
|
||||
#> ('tasks', [User(name='Jason', age=10), User(name='John', age=30)])
|
||||
|
||||
>>> name="Jason" "age"=10
|
||||
>>> name="John" "age"=10
|
||||
#> name="Jason" "age"=10
|
||||
#> name="John" "age"=10
|
||||
```
|
||||
|
||||
## Streaming Tasks
|
||||
@@ -67,15 +92,24 @@ We can also generate tasks as the tokens are streamed in by defining an `Iterabl
|
||||
Lets look at an example in action with the same class
|
||||
|
||||
```python hl_lines="6 26"
|
||||
import instructor
|
||||
import openai
|
||||
from typing import Iterable
|
||||
from pydantic import BaseModel
|
||||
|
||||
client = instructor.patch(openai.OpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
Users = Iterable[User]
|
||||
|
||||
users = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
temperature=0.1,
|
||||
stream=True,
|
||||
response_model=Users,
|
||||
response_model=Iterable[User],
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
@@ -83,22 +117,16 @@ users = client.chat.completions.create(
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
f"Consider the data below:\n{input}"
|
||||
"Correctly segment it into entitites"
|
||||
"Make sure the JSON is correct"
|
||||
),
|
||||
"content": (f"Extract `Jason is 10 and John is 10`"),
|
||||
},
|
||||
],
|
||||
max_tokens=1000,
|
||||
)
|
||||
|
||||
for user in users:
|
||||
assert isinstance(user, User)
|
||||
print(user)
|
||||
|
||||
>>> name="Jason" "age"=10
|
||||
>>> name="John" "age"=10
|
||||
#> name='Jason' age=10
|
||||
#> name='John' age=10
|
||||
```
|
||||
|
||||
## Asynchronous Streaming
|
||||
@@ -106,15 +134,36 @@ for user in users:
|
||||
I also just want to call out in this example that `instructor` also supports asynchronous streaming. This is useful when you want to stream a response model and process the results as they come in, but you'll need to use the `async for` syntax to iterate over the results.
|
||||
|
||||
```python
|
||||
model = await client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
response_model=Iterable[UserExtract],
|
||||
max_retries=2,
|
||||
stream=stream,
|
||||
messages=[
|
||||
{"role": "user", "content": "Make two up people"},
|
||||
],
|
||||
)
|
||||
async for m in model:
|
||||
assert isinstance(m, UserExtract)
|
||||
import instructor
|
||||
import openai
|
||||
from typing import Iterable
|
||||
from pydantic import BaseModel
|
||||
|
||||
client = instructor.patch(openai.AsyncOpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
|
||||
class UserExtract(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
async def print_iterable_results():
|
||||
model = await client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
response_model=Iterable[UserExtract],
|
||||
max_retries=2,
|
||||
stream=True,
|
||||
messages=[
|
||||
{"role": "user", "content": "Make two up people"},
|
||||
],
|
||||
)
|
||||
async for m in model:
|
||||
print(m)
|
||||
#> name='John Doe' age=30
|
||||
#> name='Jane Smith' age=25
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
asyncio.run(print_iterable_results())
|
||||
```
|
||||
|
||||
+41
-40
@@ -1,6 +1,6 @@
|
||||
# Handling Missing Data
|
||||
|
||||
The `Maybe` pattern is a concept in functional programming used for error handling. Instead of raising exceptions or returning `None`, you can use a `Maybe` type to encapsulate both the result and potential errors.
|
||||
The `Maybe` pattern is a concept in functional programming used for error handling. Instead of raising exceptions or returning `None`, you can use a `Maybe` type to encapsulate both the result and potential errors.
|
||||
|
||||
This pattern is particularly useful when making LLM calls, as providing language models with an escape hatch can effectively reduce hallucinations.
|
||||
|
||||
@@ -9,7 +9,8 @@ This pattern is particularly useful when making LLM calls, as providing language
|
||||
Using Pydantic, we'll first define the `UserDetail` and `MaybeUser` classes.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
@@ -35,14 +36,31 @@ Once we have the model defined, we can create a function that uses the `Maybe` p
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
import openai
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
# This enables the `response_model` keyword
|
||||
client = instructor.patch(OpenAI())
|
||||
client = instructor.patch(openai.OpenAI())
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
role: Optional[str] = Field(default=None)
|
||||
|
||||
|
||||
class MaybeUser(BaseModel):
|
||||
result: Optional[UserDetail] = Field(default=None)
|
||||
error: bool = Field(default=False)
|
||||
message: Optional[str] = Field(default=None)
|
||||
|
||||
def __bool__(self):
|
||||
return self.result is not None
|
||||
|
||||
|
||||
def extract(content: str) -> MaybeUser:
|
||||
return openai.chat.completions.create(
|
||||
return client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=MaybeUser,
|
||||
messages=[
|
||||
@@ -52,47 +70,30 @@ def extract(content: str) -> MaybeUser:
|
||||
|
||||
|
||||
user1 = extract("Jason is a 25-year-old scientist")
|
||||
# output:
|
||||
print(user1.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"result": {"age": 25, "name": "Jason", "role": "scientist"},
|
||||
"error": false,
|
||||
"message": null,
|
||||
"result": {
|
||||
"age": 25,
|
||||
"name": "Jason",
|
||||
"role": "scientist"
|
||||
},
|
||||
"error": false,
|
||||
"message": null
|
||||
}
|
||||
"""
|
||||
|
||||
user2 = extract("Unknown user")
|
||||
# output:
|
||||
{"result": null, "error": true, "message": "User not found"}
|
||||
print(user2.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"result": null,
|
||||
"error": false,
|
||||
"message": "Unknown user"
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
As you can see, when the data is extracted successfully, the `result` field contains the `UserDetail` instance. When an error occurs, the `error` field is set to `True`, and the `message` field contains the error message.
|
||||
|
||||
## Handling the result
|
||||
|
||||
There are a few ways we can handle the result. Normally, we can just access the individual fields.
|
||||
|
||||
```python
|
||||
def process_user_detail(maybe_user: MaybeUser):
|
||||
if not maybe_user.error:
|
||||
user = maybe_user.result
|
||||
print(f"User {user.name} is {user.age} years old")
|
||||
else:
|
||||
print(f"Not found: {user1.message}")
|
||||
```
|
||||
|
||||
### Pattern Matching
|
||||
|
||||
We can also use pattern matching to handle the result. This is a great way to handle errors in a structured way.
|
||||
|
||||
```python
|
||||
def process_user_detail(maybe_user: MaybeUser):
|
||||
match maybe_user:
|
||||
case MaybeUser(error=True, message=msg):
|
||||
print(f"Error: {msg}")
|
||||
case MaybeUser(result=user_detail) if user_detail:
|
||||
assert isinstance(user_detail, UserDetail)
|
||||
print(f"User {user_detail.name} is {user_detail.age} years old")
|
||||
case _:
|
||||
print("Unknown error")
|
||||
```
|
||||
|
||||
If you want to learn more about pattern matching, check out Pydantic's docs on [Structural Pattern Matching](https://docs.pydantic.dev/latest/concepts/models/#structural-pattern-matching)
|
||||
|
||||
+41
-30
@@ -2,7 +2,8 @@
|
||||
|
||||
Defining LLM output schemas in Pydantic is done via `pydantic.BaseModel`. To learn more about models in Pydantic, check out their [documentation](https://docs.pydantic.dev/latest/concepts/models/).
|
||||
|
||||
After defining a Pydantic model, we can use it as the `response_model` in your client `create` calls to OpenAI or any other supported model. The job of the `response_model` parameter is to:
|
||||
After defining a Pydantic model, we can use it as the `response_model` in your client `create` calls to OpenAI or any other supported model. The job of the `response_model` parameter is to:
|
||||
|
||||
- Define the schema and prompts for the language model
|
||||
- Validate the response from the API
|
||||
- Return a Pydantic model instance.
|
||||
@@ -32,6 +33,10 @@ Here all docstrings, types, and field annotations will be used to generate the p
|
||||
If we use `Optional` and `default`, they will be considered not required when sent to the language model
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
name: str = Field(description="The name of the user.")
|
||||
age: int = Field(description="The age of the user.")
|
||||
@@ -77,6 +82,9 @@ print(BarModel.model_fields.keys())
|
||||
We can then use this information to create the model.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, create_model
|
||||
from typing import List
|
||||
|
||||
types = {
|
||||
'string': str,
|
||||
'integer': int,
|
||||
@@ -85,43 +93,42 @@ print(BarModel.model_fields.keys())
|
||||
'List[str]': List[str],
|
||||
}
|
||||
|
||||
# Mocked cursor.fetchall()
|
||||
cursor = [
|
||||
('name', 'string', 'The name of the user.'),
|
||||
('age', 'integer', 'The age of the user.'),
|
||||
('email', 'string', 'The email of the user.'),
|
||||
]
|
||||
|
||||
BarModel = create_model(
|
||||
'User',
|
||||
**{
|
||||
property_name: (types[property_type], description)
|
||||
for property_name, property_type, description in cursor.fetchall()
|
||||
for property_name, property_type, description in cursor
|
||||
},
|
||||
__base__=BaseModel,
|
||||
)
|
||||
|
||||
print(BarModel.model_json_schema())
|
||||
"""
|
||||
{
|
||||
'properties': {
|
||||
'name': {'default': 'The name of the user.', 'title': 'Name', 'type': 'string'},
|
||||
'age': {'default': 'The age of the user.', 'title': 'Age', 'type': 'integer'},
|
||||
'email': {
|
||||
'default': 'The email of the user.',
|
||||
'title': 'Email',
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'title': 'User',
|
||||
'type': 'object',
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
This would be useful when different users have different descriptions for the same model. We can use the same model but have different prompts for each user.
|
||||
|
||||
## Structural Pattern Matching
|
||||
|
||||
Pydantic supports structural pattern matching for models, as introduced by [PEP 636](https://peps.python.org/pep-0636/) in Python 3.10.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Pet(BaseModel):
|
||||
name: str
|
||||
species: str
|
||||
|
||||
|
||||
a = Pet(name='Bones', species='dog')
|
||||
|
||||
match a:
|
||||
# match `species` to 'dog', declare and initialize `dog_name`
|
||||
case Pet(species='dog', name=dog_name):
|
||||
print(f'{dog_name} is a dog')
|
||||
#> Bones is a dog
|
||||
# default case
|
||||
case _:
|
||||
print('No dog matched')
|
||||
```
|
||||
|
||||
## Adding Behavior
|
||||
|
||||
We can add methods to our Pydantic models, just as any plain Python class. We might want to do this to add some custom logic to our models.
|
||||
@@ -142,11 +149,15 @@ class SearchQuery(BaseModel):
|
||||
query_type: Literal["web", "image", "video"]
|
||||
|
||||
def execute(self):
|
||||
# do some logic here
|
||||
return results
|
||||
print(f"Searching for {self.query} of type {self.query_type}")
|
||||
#> Searching for cat of type image
|
||||
|
||||
|
||||
query = client.chat.completions.create(..., response_model=SearchQuery)
|
||||
query = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Search for a picture of a cat"}],
|
||||
response_model=SearchQuery,
|
||||
)
|
||||
|
||||
results = query.execute()
|
||||
```
|
||||
|
||||
@@ -28,10 +28,7 @@ class GoogleSearch(BaseModel):
|
||||
query: str
|
||||
|
||||
|
||||
client = instructor.patch(
|
||||
openai.OpenAI(),
|
||||
mode=instructor.Mode.PARALLEL_TOOLS #(1)!
|
||||
)
|
||||
client = instructor.patch(openai.OpenAI(), mode=instructor.Mode.PARALLEL_TOOLS) # (1)!
|
||||
|
||||
function_calls = client.chat.completions.create(
|
||||
model="gpt-4-turbo-preview",
|
||||
@@ -42,21 +39,17 @@ function_calls = client.chat.completions.create(
|
||||
"content": "What is the weather in toronto and dallas and who won the super bowl?",
|
||||
},
|
||||
],
|
||||
response_model=Iterable[Weather | GoogleSearch], #(2)!
|
||||
response_model=Iterable[Weather | GoogleSearch], # (2)!
|
||||
)
|
||||
|
||||
for fc in function_calls:
|
||||
print(fc)
|
||||
"""
|
||||
#> location='Toronto' units='metric'
|
||||
#> location='Dallas' units='imperial'
|
||||
#> query='Super Bowl winner'
|
||||
```
|
||||
|
||||
1. Set the mode to `PARALLEL_TOOLS` to enable parallel function calling.
|
||||
2. Set the response model to `Iterable[Weather | GoogleSearch]` to indicate that the response will be a list of `Weather` and `GoogleSearch` objects. This is necessary because the response will be a list of objects, and we need to specify the types of the objects in the list.
|
||||
|
||||
```python
|
||||
Weather(location='toronto', units='imperial')
|
||||
Weather(location='dallas', units='imperial')
|
||||
GoogleSearch(query='who won the super bowl?')
|
||||
```
|
||||
|
||||
Noticed that the `response_model` Must be in the form `Iterable[Type1 | Type2 | ...]` or `Iterable[Type1]` where `Type1` and `Type2` are the types of the objects that will be returned in the response.
|
||||
|
||||
+6717
-14
File diff suppressed because it is too large
Load Diff
@@ -15,19 +15,19 @@ There are three methods for structured output:
|
||||
## Function Calling
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.patch(OpenAI())
|
||||
client = instructor.patch(OpenAI(), mode=instructor.Mode.FUNCTIONS)
|
||||
```
|
||||
|
||||
## Tool Calling
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from instructor import Mode
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.patch(OpenAI(), mode=Mode.TOOLS)
|
||||
client = instructor.patch(OpenAI(), mode=instructor.Mode.TOOLS)
|
||||
```
|
||||
|
||||
## JSON Mode
|
||||
@@ -48,11 +48,11 @@ client = instructor.patch(OpenAI(), mode=Mode.JSON)
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from instructor import Mode
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.patch(OpenAI(), mode=Mode.MD_JSON)
|
||||
client = instructor.patch(OpenAI(), mode=instructor.Mode.MD_JSON)
|
||||
```
|
||||
|
||||
### Schema Integration
|
||||
|
||||
In JSON Mode, the schema is part of the system message:
|
||||
@@ -63,6 +63,12 @@ from openai import OpenAI
|
||||
|
||||
client = instructor.patch(OpenAI())
|
||||
|
||||
|
||||
class UserExtract(instructor.OpenAISchema):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo-1106",
|
||||
response_format={"type": "json_object"},
|
||||
@@ -77,7 +83,7 @@ response = client.chat.completions.create(
|
||||
},
|
||||
],
|
||||
)
|
||||
user = UserExtract.from_response(response, mode=Mode.JSON)
|
||||
assert user.name.lower() == "jason"
|
||||
assert user.age == 25
|
||||
user = UserExtract.from_response(response, mode=instructor.Mode.JSON)
|
||||
print(user)
|
||||
#> name='Jason' age=25
|
||||
```
|
||||
|
||||
@@ -37,6 +37,7 @@ Use Python's Optional type and set a default value to prevent undesired defaults
|
||||
|
||||
```python hl_lines="6"
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
@@ -50,6 +51,10 @@ class UserDetail(BaseModel):
|
||||
You can create a wrapper class to hold either the result of an operation or an error message. This allows you to remain within a function call even if an error occurs, facilitating better error handling without breaking the code flow.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
@@ -73,6 +78,13 @@ You can further simplify this using instructor to create the `Maybe` pattern dyn
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
|
||||
|
||||
MaybeUser = instructor.Maybe(UserDetail)
|
||||
```
|
||||
@@ -85,6 +97,7 @@ To prevent data misalignment, use Enums for standardized fields. Always include
|
||||
|
||||
```python hl_lines="7 12"
|
||||
from enum import Enum, auto
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Role(Enum):
|
||||
@@ -105,6 +118,10 @@ class UserDetail(BaseModel):
|
||||
If you're having a hard time with `Enum` and alternative is to use `Literal`
|
||||
|
||||
```python hl_lines="4"
|
||||
from typing import Literal
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
@@ -118,6 +135,9 @@ If you'd like to improve performance more you can reiterate the requirements in
|
||||
For complex attributes, it helps to reiterate the instructions in the field's description.
|
||||
|
||||
```python hl_lines="5 11"
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Role(BaseModel):
|
||||
"""
|
||||
Extract the role based on the following rules ...
|
||||
@@ -142,6 +162,7 @@ When you need to extract undefined attributes, use a list of key-value pairs.
|
||||
|
||||
```python hl_lines="10"
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Property(BaseModel):
|
||||
@@ -162,6 +183,10 @@ class UserDetail(BaseModel):
|
||||
When dealing with lists of attributes, especially arbitrary properties, it's crucial to manage the length. You can use prompting and enumeration to limit the list length, ensuring a manageable set of properties.
|
||||
|
||||
```python hl_lines="2 9"
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Property(BaseModel):
|
||||
index: str = Field(..., description="Monotonically increasing ID")
|
||||
key: str
|
||||
@@ -182,6 +207,10 @@ class UserDetail(BaseModel):
|
||||
For simple types, tuples can be a more compact alternative to custom classes, especially when the properties don't require additional descriptions.
|
||||
|
||||
```python hl_lines="4"
|
||||
from typing import List, Tuple
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
@@ -196,6 +225,16 @@ class UserDetail(BaseModel):
|
||||
For multiple users, aim to use consistent key names when extracting properties.
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
id: int
|
||||
age: int
|
||||
name: str
|
||||
|
||||
|
||||
class UserDetails(BaseModel):
|
||||
"""
|
||||
Extract information for multiple users.
|
||||
@@ -212,6 +251,10 @@ This refined guide should offer a cleaner and more organized approach to structu
|
||||
In cases where relationships exist between entities, it's vital to define them explicitly in the model. The following example demonstrates how to define relationships between users by incorporating an id and a friends field:
|
||||
|
||||
```python hl_lines="2 5 8"
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
id: int = Field(..., description="Unique identifier for each user.")
|
||||
age: int
|
||||
@@ -234,6 +277,9 @@ class UserRelationships(BaseModel):
|
||||
You can reuse the same component for different contexts within a model. In this example, the TimeRange component is used for both work_time and leisure_time.
|
||||
|
||||
```python hl_lines="9 10"
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TimeRange(BaseModel):
|
||||
start_time: int = Field(..., description="The start time in hours.")
|
||||
end_time: int = Field(..., description="The end time in hours.")
|
||||
@@ -254,6 +300,9 @@ class UserDetail(BaseModel):
|
||||
Sometimes, a component like TimeRange may require some context or additional logic to be used effectively. Employing a "chain of thought" field within the component can help in understanding or optimizing the time range allocations.
|
||||
|
||||
```python hl_lines="2"
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TimeRange(BaseModel):
|
||||
chain_of_thought: str = Field(
|
||||
..., description="Step by step reasoning to get the correct time range"
|
||||
|
||||
@@ -25,7 +25,7 @@ user: UserExtract = client.chat.completions.create(
|
||||
print(user._raw_response)
|
||||
"""
|
||||
ChatCompletion(
|
||||
id='chatcmpl-8owwph3BaKJddZKqPIOygvQy1CmLu',
|
||||
id='chatcmpl-8oz1eZxBVDCUZu7Q247DenDK8T3ji',
|
||||
choices=[
|
||||
Choice(
|
||||
finish_reason='stop',
|
||||
@@ -42,7 +42,7 @@ ChatCompletion(
|
||||
),
|
||||
)
|
||||
],
|
||||
created=1707153687,
|
||||
created=1707161674,
|
||||
model='gpt-3.5-turbo-0613',
|
||||
object='chat.completion',
|
||||
system_fingerprint=None,
|
||||
@@ -56,32 +56,3 @@ ChatCompletion(
|
||||
This is the recommended way to access the tokens usage, since it is a pydantic model you can use any of the pydantic model methods on it. For example, you can access the `total_tokens` by doing `user._raw_response.usage.total_tokens`. Note that this also includes the tokens used during any previous unsuccessful attempts.
|
||||
|
||||
In the future, we may add additional hooks to the `raw_response` to make it easier to access the tokens usage.
|
||||
|
||||
**Output:**
|
||||
|
||||
```python
|
||||
{
|
||||
"id": "chatcmpl-8bHUPGZc9vAXBraJlebf8ciz4AMuh",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content": null,
|
||||
"role": "assistant",
|
||||
"function_call": {
|
||||
"arguments": "{\n \"name\": \"Jason\",\n \"age\": 25\n}",
|
||||
"name": "UserExtract",
|
||||
},
|
||||
"tool_calls": null,
|
||||
},
|
||||
"logprobs": null,
|
||||
}
|
||||
],
|
||||
"created": 1703896057,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": null,
|
||||
"usage": {"completion_tokens": 16, "prompt_tokens": 73, "total_tokens": 89},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -111,6 +111,7 @@ answer
|
||||
Validators are a great tool for ensuring some property of the outputs. When you use the `patch()` method with the `openai` client, you can use the `max_retries` parameter to set the number of times you can reask the model to correct the output.
|
||||
|
||||
It is a great layer of defense against bad outputs of two forms:
|
||||
|
||||
1. Pydantic Validation Errors (code or llm based)
|
||||
2. JSON Decoding Errors (when the model returns a bad response)
|
||||
|
||||
@@ -118,12 +119,13 @@ It is a great layer of defense against bad outputs of two forms:
|
||||
|
||||
Notice that the field validator wants the name in uppercase, but the user input is lowercase. The validator will raise a `ValueError` if the name is not in uppercase.
|
||||
|
||||
```python hl_lines="11-16"
|
||||
```python hl_lines="12-17"
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel, field_validator
|
||||
|
||||
# Apply the patch to the OpenAI client
|
||||
client = instructor.patch(OpenAI())
|
||||
client = instructor.patch(openai.OpenAI())
|
||||
|
||||
|
||||
class UserDetails(BaseModel):
|
||||
@@ -142,7 +144,19 @@ class UserDetails(BaseModel):
|
||||
|
||||
Here, the `UserDetails` model is passed as the `response_model`, and `max_retries` is set to 2.
|
||||
|
||||
```python hl_lines="4 10"
|
||||
```python
|
||||
import instructor
|
||||
import openai
|
||||
from pydantic import BaseModel
|
||||
|
||||
client = instructor.patch(openai.OpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
|
||||
class UserDetails(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
model = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserDetails,
|
||||
@@ -152,7 +166,13 @@ model = client.chat.completions.create(
|
||||
],
|
||||
)
|
||||
|
||||
assert model.name == "JASON"
|
||||
print(model.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "jason",
|
||||
"age": 25
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
### What happens behind the scenes?
|
||||
@@ -160,9 +180,12 @@ assert model.name == "JASON"
|
||||
Behind the scenes, the `instructor.patch()` method adds a `max_retries` parameter to the `openai.ChatCompletion.create()` method. The `max_retries` parameter will trigger up to 2 reattempts if the `name` attribute fails the uppercase validation in `UserDetails`.
|
||||
|
||||
```python
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
try:
|
||||
...
|
||||
except (ValidationError, JSONDecodeError) as e:
|
||||
except ValidationError as e:
|
||||
kwargs["messages"].append(response.choices[0].message)
|
||||
kwargs["messages"].append(
|
||||
{
|
||||
@@ -175,6 +198,7 @@ except (ValidationError, JSONDecodeError) as e:
|
||||
## Advanced Validation Techniques
|
||||
|
||||
The docs are currently incomplete, but we have a few advanced validation techniques that we're working on documenting better such as model level validation, and using a validation context. Check out our example on [verifying citations](../examples/exact_citations.md) which covers:
|
||||
|
||||
1. Validate the entire object with all attributes rather than one attribute at a time
|
||||
2. Using some 'context' to validate the object: In this case, we use the `context` to check if the citation existed in the original text.
|
||||
|
||||
|
||||
+69
-16
@@ -24,15 +24,18 @@ def uppercase_validator(v):
|
||||
class UserDetail(BaseModel):
|
||||
name: Annotated[str, AfterValidator(uppercase_validator)]
|
||||
age: int
|
||||
```
|
||||
|
||||
Now if we create a user detail with a lowercase name, we'll see an error.
|
||||
|
||||
```python
|
||||
UserDetail(name="jason", age=12)
|
||||
>>> 1 validation error for UserDetail
|
||||
>>> name
|
||||
>>> Value error, Name must be ALL CAPS [type=value_error, input_value='jason', input_type=str]
|
||||
try:
|
||||
UserDetail(name="jason", age=12)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for UserDetail
|
||||
name
|
||||
Value error, Name must be ALL CAPS [type=value_error, input_value='jason', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.6/v/value_error
|
||||
"""
|
||||
```
|
||||
|
||||
## Simple: Max Retries
|
||||
@@ -40,6 +43,16 @@ UserDetail(name="jason", age=12)
|
||||
The simplest way of defining a retry is just defining the maximum number of retries.
|
||||
|
||||
```python
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
client = instructor.patch(openai.OpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
@@ -50,19 +63,19 @@ response = client.chat.completions.create(
|
||||
],
|
||||
max_retries=3, # (1)!
|
||||
)
|
||||
assert response.name == "JASON" # (2)!
|
||||
print(response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "jason",
|
||||
"age": 12
|
||||
}
|
||||
"""
|
||||
# (2)!
|
||||
```
|
||||
|
||||
1. We set the maximum number of retries to 3. This means that if the model returns an error, we'll reask the model up to 3 times.
|
||||
2. We assert that the name is in all caps.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "JASON",
|
||||
"age": 12
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced: Retry Logic
|
||||
|
||||
If you want more control over how we define retries such as back-offs and additional retry logic we can use a library called Tenacity. To learn more, check out the documentation on the [Tenacity](https://tenacity.readthedocs.io/en/latest/) website.
|
||||
@@ -70,8 +83,19 @@ If you want more control over how we define retries such as back-offs and additi
|
||||
Rather than using the decorator `@retry`, we can use the `Retrying` and `AsyncRetrying` classes to define our own retry logic.
|
||||
|
||||
```python
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
from tenacity import Retrying, stop_after_attempt, wait_fixed
|
||||
|
||||
client = instructor.patch(openai.OpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4-turbo-preview",
|
||||
response_model=UserDetail,
|
||||
@@ -83,6 +107,13 @@ response = client.chat.completions.create(
|
||||
wait=wait_fixed(1), # (2)!
|
||||
), # (3)!
|
||||
)
|
||||
print(response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "jason",
|
||||
"age": 12
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
1. We stop after 2 attempts
|
||||
@@ -94,9 +125,20 @@ response = client.chat.completions.create(
|
||||
If you're using asynchronous code, you can use `AsyncRetrying` instead.
|
||||
|
||||
```python
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
from tenacity import AsyncRetrying, stop_after_attempt, wait_fixed
|
||||
|
||||
response = await client.chat.completions.create(
|
||||
client = instructor.patch(openai.AsyncOpenAI(), mode=instructor.Mode.TOOLS)
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
task = client.chat.completions.create(
|
||||
model="gpt-4-turbo-preview",
|
||||
response_model=UserDetail,
|
||||
messages=[
|
||||
@@ -107,6 +149,17 @@ response = await client.chat.completions.create(
|
||||
wait=wait_fixed(1),
|
||||
),
|
||||
)
|
||||
|
||||
import asyncio
|
||||
|
||||
response = asyncio.run(task)
|
||||
print(response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "jason",
|
||||
"age": 12
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
## Other Features of Tenacity
|
||||
|
||||
@@ -7,6 +7,10 @@ While many libraries support multiple function calls, and tool calls support mul
|
||||
You can use `Union` types to write _agents_ that can dynamically choose actions - by choosing an output class. For example, in a search and lookup function, the LLM can determine whether to execute another search, lookup or other action.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Search(BaseModel):
|
||||
query: str
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ Response(message="I want to make them suffer the consequences")
|
||||
|
||||
The validator will raise a `ValidationError` if the content violates the policies, like so:
|
||||
|
||||
```python
|
||||
```plaintext
|
||||
ValidationError: 1 validation error for Response
|
||||
message
|
||||
Value error, `I want to make them suffer the consequences` was flagged for harassment, harassment_threatening, violence, harassment/threatening [type=value_error, input_value='I want to make them suffer the consequences', input_type=str]
|
||||
|
||||
@@ -154,7 +154,7 @@ In this example, we call `parse_tree_to_filesystem` with a string representing a
|
||||
|
||||
After parsing the string into a `DirectoryTree` object, we call `root.print_paths()` to print the paths of the root node and its children. The output of this example will be:
|
||||
|
||||
```python
|
||||
```plaintext
|
||||
root NodeType.FOLDER
|
||||
root/folder1 NodeType.FOLDER
|
||||
root/folder1/file1.txt NodeType.FILE
|
||||
|
||||
+62
-7
@@ -47,6 +47,13 @@ user = client.chat.completions.create(
|
||||
assert isinstance(user, UserDetail)
|
||||
assert user.name == "Jason"
|
||||
assert user.age == 25
|
||||
print(user.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "Jason",
|
||||
"age": 25
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
**Using async clients**
|
||||
@@ -54,6 +61,7 @@ assert user.age == 25
|
||||
For async clients you must use `apatch` vs `patch` like so:
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
import instructor
|
||||
from openai import AsyncOpenAI
|
||||
from pydantic import BaseModel
|
||||
@@ -66,7 +74,7 @@ class UserExtract(BaseModel):
|
||||
age: int
|
||||
|
||||
|
||||
model = await aclient.chat.completions.create(
|
||||
task = aclient.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserExtract,
|
||||
messages=[
|
||||
@@ -74,15 +82,34 @@ model = await aclient.chat.completions.create(
|
||||
],
|
||||
)
|
||||
|
||||
assert isinstance(model, UserExtract)
|
||||
response = asyncio.run(task)
|
||||
print(response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"name": "Jason",
|
||||
"age": 25
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
!!! note "Accessing the original response"
|
||||
!!! note "Accessing the original response and usage tokens"
|
||||
|
||||
If you want to access anything like usage or other metadata, the original response is available on the `Model._raw_response` attribute.
|
||||
|
||||
```python
|
||||
user: UserDetail = client.chat.completions.create(
|
||||
import openai
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
|
||||
client = instructor.patch(openai.OpenAI())
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
user = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=UserDetail,
|
||||
messages=[
|
||||
@@ -90,9 +117,37 @@ assert isinstance(model, UserExtract)
|
||||
],
|
||||
)
|
||||
|
||||
from openai.types.chat.chat_completion import ChatCompletion
|
||||
|
||||
assert isinstance(user._raw_response, ChatCompletion)
|
||||
print(user._raw_response.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"id": "chatcmpl-8oz12FZ9fvCypa8yNuKbHXueJQkOv",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"logprobs": null,
|
||||
"message": {
|
||||
"content": null,
|
||||
"role": "assistant",
|
||||
"function_call": {
|
||||
"arguments": "{\n \"name\": \"Jason\",\n \"age\": 25\n}",
|
||||
"name": "UserDetail"
|
||||
},
|
||||
"tool_calls": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"created": 1707161636,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": null,
|
||||
"usage": {
|
||||
"completion_tokens": 16,
|
||||
"prompt_tokens": 72,
|
||||
"total_tokens": 88
|
||||
}
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
## Why use Instructor?
|
||||
|
||||
+2
-2
@@ -193,8 +193,8 @@ for user in users:
|
||||
assert isinstance(user, User)
|
||||
print(user)
|
||||
|
||||
>>> name="Jason" "age"=10
|
||||
>>> name="John" "age"=10
|
||||
#> name="Jason" "age"=10
|
||||
#> name="John" "age"=10
|
||||
```
|
||||
|
||||
## Partial Extraction
|
||||
|
||||
@@ -125,7 +125,7 @@ class JSONParser:
|
||||
s = s[end + 1 :]
|
||||
return json.loads(str_val), s
|
||||
|
||||
def parse_number(self, s):
|
||||
def parse_number(self, s, e):
|
||||
i = 0
|
||||
while i < len(s) and s[i] in "0123456789.-":
|
||||
i += 1
|
||||
|
||||
+5
-9
@@ -297,7 +297,7 @@ async def retry_async(
|
||||
stop=stop_after_attempt(max_retries),
|
||||
reraise=True,
|
||||
)
|
||||
if not isinstance(max_retries, AsyncRetrying):
|
||||
if not isinstance(max_retries, (AsyncRetrying, Retrying)):
|
||||
raise ValueError(
|
||||
"max_retries must be an `int` or a `tenacity.AsyncRetrying` object"
|
||||
)
|
||||
@@ -318,9 +318,7 @@ async def retry_async(
|
||||
)
|
||||
total_usage.prompt_tokens += response.usage.prompt_tokens or 0
|
||||
total_usage.total_tokens += response.usage.total_tokens or 0
|
||||
response.usage = (
|
||||
total_usage # Replace each response usage with the total usage
|
||||
)
|
||||
response.usage = total_usage # Replace each response usage with the total usage
|
||||
return await process_response_async(
|
||||
response,
|
||||
response_model=response_model,
|
||||
@@ -383,8 +381,8 @@ def retry_sync(
|
||||
stop=stop_after_attempt(max_retries),
|
||||
reraise=True,
|
||||
)
|
||||
if not isinstance(max_retries, Retrying):
|
||||
raise ValueError("max_retries must be an int or a `tenacityRetrying` object")
|
||||
if not isinstance(max_retries, (Retrying, AsyncRetrying)):
|
||||
raise ValueError("max_retries must be an int or a `tenacity.Retrying` object")
|
||||
|
||||
try:
|
||||
for attempt in max_retries:
|
||||
@@ -401,9 +399,7 @@ def retry_sync(
|
||||
)
|
||||
total_usage.prompt_tokens += response.usage.prompt_tokens or 0
|
||||
total_usage.total_tokens += response.usage.total_tokens or 0
|
||||
response.usage = (
|
||||
total_usage # Replace each response usage with the total usage
|
||||
)
|
||||
response.usage = total_usage # Replace each response usage with the total usage
|
||||
return process_response(
|
||||
response,
|
||||
response_model=response_model,
|
||||
|
||||
Generated
+399
-230
@@ -187,15 +187,61 @@ files = [
|
||||
[package.extras]
|
||||
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.1.1"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"},
|
||||
{file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"},
|
||||
{file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"},
|
||||
{file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"},
|
||||
{file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"},
|
||||
{file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"},
|
||||
{file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"},
|
||||
{file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"},
|
||||
{file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"},
|
||||
{file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"},
|
||||
{file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"},
|
||||
{file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"},
|
||||
{file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"},
|
||||
{file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"},
|
||||
{file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"},
|
||||
{file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"},
|
||||
{file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"},
|
||||
{file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"},
|
||||
{file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"},
|
||||
{file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"},
|
||||
{file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"},
|
||||
{file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.11.17"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
|
||||
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -386,6 +432,17 @@ files = [
|
||||
[package.extras]
|
||||
toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "diskcache"
|
||||
version = "5.6.3"
|
||||
description = "Disk Cache -- Disk and file backed persistent cache."
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
files = [
|
||||
{file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"},
|
||||
{file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distro"
|
||||
version = "1.9.0"
|
||||
@@ -422,6 +479,25 @@ files = [
|
||||
[package.extras]
|
||||
test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.109.2"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"},
|
||||
{file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
||||
starlette = ">=0.36.3,<0.37.0"
|
||||
typing-extensions = ">=4.8.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.4.1"
|
||||
@@ -675,71 +751,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"},
|
||||
{file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -811,13 +887,13 @@ mkdocs = ">=1.1"
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.5.6"
|
||||
version = "9.5.7"
|
||||
description = "Documentation that simply works"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mkdocs_material-9.5.6-py3-none-any.whl", hash = "sha256:e115b90fccf5cd7f5d15b0c2f8e6246b21041628b8f590630e7fca66ed7fcf6c"},
|
||||
{file = "mkdocs_material-9.5.6.tar.gz", hash = "sha256:5b24df36d8ac6cecd611241ce6f6423ccde3e1ad89f8360c3f76d5565fc2d82a"},
|
||||
{file = "mkdocs_material-9.5.7-py3-none-any.whl", hash = "sha256:0be8ce8bcfebb52bae9b00cf9b851df45b8a92d629afcfd7f2c09b2dfa155ea3"},
|
||||
{file = "mkdocs_material-9.5.7.tar.gz", hash = "sha256:16110292575d88a338d2961f3cb665cf12943ff8829e551a9b364f24019e46af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -890,85 +966,101 @@ mkdocstrings = ">=0.20"
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.0.4"
|
||||
version = "6.0.5"
|
||||
description = "multidict implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
|
||||
{file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
|
||||
{file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
|
||||
{file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
|
||||
{file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
|
||||
{file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
|
||||
{file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
|
||||
{file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
|
||||
{file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1031,13 +1123,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.10.0"
|
||||
version = "1.11.1"
|
||||
description = "The official Python library for the openai API"
|
||||
optional = false
|
||||
python-versions = ">=3.7.1"
|
||||
files = [
|
||||
{file = "openai-1.10.0-py3-none-any.whl", hash = "sha256:aa69e97d0223ace9835fbf9c997abe9ee95318f684fd2de6d02c870700c71ebc"},
|
||||
{file = "openai-1.10.0.tar.gz", hash = "sha256:208886cb501b930dc63f48d51db9c15e5380380f80516d07332adad67c9f1053"},
|
||||
{file = "openai-1.11.1-py3-none-any.whl", hash = "sha256:e0f388ce499f53f58079d0c1f571f356f2b168b84d0d24a412506b6abc714980"},
|
||||
{file = "openai-1.11.1.tar.gz", hash = "sha256:f66b8fe431af43e09594147ef3cdcb79758285de72ebafd52be9700a2af41e99"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1116,18 +1208,18 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"},
|
||||
{file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"},
|
||||
{file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"},
|
||||
{file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.16.1"
|
||||
pydantic-core = "2.16.2"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
@@ -1135,90 +1227,90 @@ email = ["email-validator (>=2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.16.1"
|
||||
version = "2.16.2"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"},
|
||||
{file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"},
|
||||
{file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"},
|
||||
{file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f"},
|
||||
{file = "pydantic_core-2.16.2-cp38-none-win32.whl", hash = "sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a"},
|
||||
{file = "pydantic_core-2.16.2-cp38-none-win_amd64.whl", hash = "sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2"},
|
||||
{file = "pydantic_core-2.16.2-cp39-none-win32.whl", hash = "sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469"},
|
||||
{file = "pydantic_core-2.16.2-cp39-none-win_amd64.whl", hash = "sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"},
|
||||
{file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1297,6 +1389,22 @@ pytest = ">=7.0.0"
|
||||
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
||||
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-examples"
|
||||
version = "0.0.10"
|
||||
description = "Pytest plugin for testing examples in docstrings and markdown files."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pytest_examples-0.0.10-py3-none-any.whl", hash = "sha256:3d0b52424e454846beed8621a12b85db88c6c17049f65c2f417211372c20dc9e"},
|
||||
{file = "pytest_examples-0.0.10.tar.gz", hash = "sha256:5d34d22e689aca2bbad8dd6b7cdcc9d0107e2942853b3154f3a3c68d145d91c5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
black = ">=23"
|
||||
pytest = ">=7"
|
||||
ruff = ">=0.0.258"
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
@@ -1384,6 +1492,24 @@ files = [
|
||||
[package.dependencies]
|
||||
pyyaml = "*"
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.1"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"},
|
||||
{file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""}
|
||||
|
||||
[package.extras]
|
||||
hiredis = ["hiredis (>=1.0.0)"]
|
||||
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2023.12.25"
|
||||
@@ -1525,6 +1651,32 @@ pygments = ">=2.13.0,<3.0.0"
|
||||
[package.extras]
|
||||
jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.2.0"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:638ea3294f800d18bae84a492cb5a245c8d29c90d19a91d8e338937a4c27fca0"},
|
||||
{file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3ff35433fcf4dff6d610738712152df6b7d92351a1bde8e00bd405b08b3d5759"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9faafbdcf4f53917019f2c230766da437d4fd5caecd12ddb68bb6a17d74399"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8153a3e4128ed770871c47545f1ae7b055023e0c222ff72a759f5a341ee06483"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a75a98ae989a27090e9c51f763990ad5bbc92d20626d54e9701c7fe597f399"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:87057dd2fdde297130ff99553be8549ca38a2965871462a97394c22ed2dfc19d"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d232f99d3ab00094ebaf88e0fb7a8ccacaa54cc7fa3b8993d9627a11e6aed7a"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3c641f95f435fc6754b05591774a17df41648f0daf3de0d75ad3d9f099ab92"},
|
||||
{file = "ruff-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3826fb34c144ef1e171b323ed6ae9146ab76d109960addca730756dc19dc7b22"},
|
||||
{file = "ruff-0.2.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eceab7d85d09321b4de18b62d38710cf296cb49e98979960a59c6b9307c18cfe"},
|
||||
{file = "ruff-0.2.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:30ad74687e1f4a9ff8e513b20b82ccadb6bd796fe5697f1e417189c5cde6be3e"},
|
||||
{file = "ruff-0.2.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7e3818698f8460bd0f8d4322bbe99db8327e9bc2c93c789d3159f5b335f47da"},
|
||||
{file = "ruff-0.2.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:edf23041242c48b0d8295214783ef543847ef29e8226d9f69bf96592dba82a83"},
|
||||
{file = "ruff-0.2.0-py3-none-win32.whl", hash = "sha256:e155147199c2714ff52385b760fe242bb99ea64b240a9ffbd6a5918eb1268843"},
|
||||
{file = "ruff-0.2.0-py3-none-win_amd64.whl", hash = "sha256:ba918e01cdd21e81b07555564f40d307b0caafa9a7a65742e98ff244f5035c59"},
|
||||
{file = "ruff-0.2.0-py3-none-win_arm64.whl", hash = "sha256:3fbaff1ba9564a2c5943f8f38bc221f04bac687cc7485e45237579fee7ccda79"},
|
||||
{file = "ruff-0.2.0.tar.gz", hash = "sha256:63856b91837606c673537d2889989733d7dffde553828d3b0f0bacfa6def54be"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
@@ -1547,6 +1699,23 @@ files = [
|
||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.36.3"
|
||||
description = "The little ASGI library that shines."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"},
|
||||
{file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.4.0,<5"
|
||||
|
||||
[package.extras]
|
||||
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "tenacity"
|
||||
version = "8.2.3"
|
||||
@@ -1786,4 +1955,4 @@ multidict = ">=4.0"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "612f87060d75d66a49e0a1256950f472768411dd7545c343b58c7265b8aaf44f"
|
||||
content-hash = "c32017c38256d401cecf4162f927215b2b3760b6f3ec0fcc2bc54ffce7a688fa"
|
||||
|
||||
@@ -30,6 +30,13 @@ mkdocstrings-python = "^1.1.2"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
coverage = "^7.3.2"
|
||||
mypy = "^1.7.1"
|
||||
pytest-examples = "^0.0.10"
|
||||
|
||||
|
||||
[tool.poetry.group.test-docs.dependencies]
|
||||
fastapi = "^0.109.2"
|
||||
redis = "^5.0.1"
|
||||
diskcache = "^5.6.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
||||
@@ -11,4 +11,4 @@ aiohttp==3.9.1
|
||||
yarl==1.8.1
|
||||
frozenlist==1.3.1
|
||||
mkdocs-minify-plugin
|
||||
mkdocs-rss-plugin
|
||||
mkdocs-rss-plugin
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import pytest
|
||||
from pytest_examples import find_examples, CodeExample, EvalExample
|
||||
|
||||
|
||||
@pytest.mark.parametrize("example", find_examples("README.md"), ids=str)
|
||||
def test_readme(example: CodeExample, eval_example: EvalExample):
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("example", find_examples("docs/"), ids=str)
|
||||
def test_readme(example: CodeExample, eval_example: EvalExample):
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
@@ -0,0 +1,54 @@
|
||||
import pytest
|
||||
from pytest_examples import find_examples, CodeExample, EvalExample
|
||||
|
||||
|
||||
@pytest.mark.parametrize("example", find_examples("README.md"), ids=str)
|
||||
def test_readme(example: CodeExample, eval_example: EvalExample):
|
||||
if eval_example.update_examples:
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
else:
|
||||
eval_example.lint(example)
|
||||
eval_example.run(example)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("example", find_examples("docs/index.md"), ids=str)
|
||||
def test_index(example: CodeExample, eval_example: EvalExample):
|
||||
if eval_example.update_examples:
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
else:
|
||||
eval_example.lint(example)
|
||||
eval_example.run(example)
|
||||
|
||||
|
||||
@pytest.mark.skip("Blogs have too many small examples")
|
||||
@pytest.mark.parametrize("example", find_examples("docs/blog"), ids=str)
|
||||
def test_format_blog(example: CodeExample, eval_example: EvalExample):
|
||||
if eval_example.update_examples:
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
else:
|
||||
eval_example.lint(example)
|
||||
eval_example.run(example)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("example", find_examples("docs/concepts"), ids=str)
|
||||
def test_format_concepts(example: CodeExample, eval_example: EvalExample):
|
||||
if eval_example.update_examples:
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
else:
|
||||
eval_example.lint(example)
|
||||
eval_example.run(example)
|
||||
|
||||
|
||||
@pytest.mark.skip("Examples are too long")
|
||||
@pytest.mark.parametrize("example", find_examples("docs/examples"), ids=str)
|
||||
def test_format_examples(example: CodeExample, eval_example: EvalExample):
|
||||
if eval_example.update_examples:
|
||||
eval_example.format(example)
|
||||
eval_example.run_print_update(example)
|
||||
else:
|
||||
eval_example.lint(example)
|
||||
eval_example.run(example)
|
||||
+1
-134
@@ -1,16 +1,9 @@
|
||||
import functools
|
||||
|
||||
import pytest
|
||||
from openai import AsyncOpenAI, OpenAI
|
||||
from openai.types.chat import ChatCompletionMessage, ChatCompletionMessageParam
|
||||
from openai.types.chat.chat_completion_message import FunctionCall
|
||||
from openai.types.chat.chat_completion_message_tool_call import (
|
||||
ChatCompletionMessageToolCall,
|
||||
Function,
|
||||
)
|
||||
|
||||
import instructor
|
||||
from instructor.patch import OVERRIDE_DOCS, dump_message, is_async
|
||||
from instructor.patch import OVERRIDE_DOCS, is_async
|
||||
|
||||
|
||||
def test_patch_completes_successfully():
|
||||
@@ -50,129 +43,3 @@ def test_override_docs():
|
||||
assert (
|
||||
"response_model" in OVERRIDE_DOCS
|
||||
), "response_model should be in OVERRIDE_DOCS"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name_of_test, message, expected",
|
||||
[
|
||||
(
|
||||
"tool_calls and content and no function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content="Hello, world!",
|
||||
tool_calls=[
|
||||
ChatCompletionMessageToolCall(
|
||||
id="test_tool",
|
||||
function=Function(arguments="", name="test_tool"),
|
||||
type="function",
|
||||
)
|
||||
],
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": 'Hello, world![{"id": "test_tool", "function": {"arguments": "", "name": "test_tool"}, "type": "function"}]',
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "test_tool",
|
||||
"function": {"arguments": "", "name": "test_tool"},
|
||||
"type": "function",
|
||||
}
|
||||
],
|
||||
},
|
||||
),
|
||||
(
|
||||
"tool_calls and no content and no function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content=None,
|
||||
tool_calls=[
|
||||
ChatCompletionMessageToolCall(
|
||||
id="test_tool",
|
||||
function=Function(arguments="", name="test_tool"),
|
||||
type="function",
|
||||
)
|
||||
],
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": '[{"id": "test_tool", "function": {"arguments": "", "name": "test_tool"}, "type": "function"}]',
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "test_tool",
|
||||
"function": {"arguments": "", "name": "test_tool"},
|
||||
"type": "function",
|
||||
}
|
||||
],
|
||||
},
|
||||
),
|
||||
(
|
||||
"no tool_calls and no content no function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content=None,
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
},
|
||||
),
|
||||
(
|
||||
"no tool_calls and content and function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content="Hello, world!",
|
||||
function_call=FunctionCall(arguments="", name="test_tool"),
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": 'Hello, world!{"arguments": "", "name": "test_tool"}',
|
||||
},
|
||||
),
|
||||
(
|
||||
"no tool_calls and no content and function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content=None,
|
||||
function_call=FunctionCall(arguments="", name="test_tool"),
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": '{"arguments": "", "name": "test_tool"}',
|
||||
},
|
||||
),
|
||||
(
|
||||
"tool_calls and no content and function_call",
|
||||
ChatCompletionMessage(
|
||||
role="assistant",
|
||||
content="",
|
||||
function_call=FunctionCall(arguments="", name="test_tool"),
|
||||
tool_calls=[
|
||||
ChatCompletionMessageToolCall(
|
||||
id="test_tool",
|
||||
function=Function(arguments="", name="test_tool"),
|
||||
type="function",
|
||||
)
|
||||
],
|
||||
),
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": '[{"id": "test_tool", "function": {"arguments": "", "name": "test_tool"}, "type": "function"}]{"arguments": "", "name": "test_tool"}',
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "test_tool",
|
||||
"function": {"arguments": "", "name": "test_tool"},
|
||||
"type": "function",
|
||||
}
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.skip("New changes to tools and functions")
|
||||
def test_dump_message(
|
||||
name_of_test: str,
|
||||
message: ChatCompletionMessage,
|
||||
expected: ChatCompletionMessageParam,
|
||||
):
|
||||
#! Something is going on right now, but I don't have time to figure it out @jxnlco
|
||||
assert dump_message(message) == expected, name_of_test
|
||||
|
||||
Reference in New Issue
Block a user