mirror of
https://github.com/ollama/ollama-python.git
synced 2026-06-16 13:14:54 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d98f646929 | |||
| 981015cfb8 | |||
| 9c34d810ab | |||
| 9f2832d2ed | |||
| e220e46320 | |||
| dfdeb7cef3 | |||
| 9e6726e28a | |||
| 10d0ff2b3a | |||
| 8b694bb0f4 | |||
| cbf0887573 | |||
| cd4fbfc2bc | |||
| d2da64f253 | |||
| ae2bdbf3cd | |||
| 60d292a98d | |||
| ab7e600b95 | |||
| 8e3a8febba | |||
| 6025c360da | |||
| f62eb97a4a | |||
| b498ab3451 | |||
| a49c9860b1 | |||
| 33c4b61ff9 | |||
| b0ea6d9e44 | |||
| 2cb796cbf5 | |||
| 359c63daa7 | |||
| 222c2079c2 | |||
| 49568d5e85 | |||
| 1a15742705 | |||
| ce56f279e8 | |||
| 982d65fea0 | |||
| d25c4aa1cf | |||
| fa7bf7cbd1 | |||
| 04d102b406 | |||
| 57c597b60e | |||
| 74db547ca4 | |||
| d5316d023a | |||
| 14d7f8c1b7 | |||
| b45848a9cf | |||
| 4fb909db7b | |||
| 0824044330 | |||
| 0f8c20a596 | |||
| cb81f522b0 | |||
| 5dc857ea66 | |||
| 309007da80 | |||
| 3b69774f22 | |||
| eaab4778c7 | |||
| 96214c0ffe | |||
| 00eafed0fa | |||
| a8cb34e0ec | |||
| 2b66adebef | |||
| cc1fca067b | |||
| 8b91188f66 | |||
| 85f526353a | |||
| 5cf83dcda8 | |||
| 2ac751fb79 | |||
| c4931b202d | |||
| 30f762ae77 | |||
| e5c4799650 | |||
| fed2b2ee02 | |||
| e403d748a3 | |||
| 30090c5508 | |||
| e1b0600088 | |||
| 029b080c4c | |||
| 5b94a8d872 | |||
| d38fe129c3 | |||
| 426b30e9ed | |||
| dc9543ba5f | |||
| 42f9f70bae | |||
| 2fde317490 | |||
| 7201dcbbd3 | |||
| dfa5b12061 | |||
| f5a8ec3272 | |||
| 3c9ba8cf2e | |||
| 768c559094 | |||
| 44126e5a37 | |||
| 841595aed1 | |||
| 281e149001 | |||
| 9c4a87c90b | |||
| adcae4ce25 | |||
| 6b351e54cf | |||
| 4896b53755 | |||
| e28f43f892 | |||
| fcdf5771f5 | |||
| ec8bf88c2b | |||
| 8b929ab496 | |||
| eee32dda37 | |||
| c74dd5835d | |||
| cdec2ad99e | |||
| 70e376368f | |||
| 4a81fa43ee | |||
| 98ad0d884e | |||
| 09ae36a0f9 | |||
| c27eebc158 | |||
| 46291d49a7 | |||
| cf3ab807c8 | |||
| 8e5d431d0d | |||
| e201181d4c | |||
| c077b5d685 | |||
| fbb6553e03 | |||
| f618a2f448 | |||
| 354f012168 | |||
| 19e7a3d9ce | |||
| 9bfa1d1ecf | |||
| 2e0b2e15f0 | |||
| 5c1df78b7d | |||
| c8f7d9b7d2 | |||
| 21aad8447c | |||
| 38f68e251d | |||
| fa6d8d5a9f | |||
| 373a2e3759 | |||
| 27f0dfe0ca | |||
| 467fd833f3 | |||
| 9c60d1569c | |||
| 43d36489b2 | |||
| 3119736eda | |||
| bae245ea63 | |||
| bf46f31936 | |||
| 81e253997d | |||
| 89c4b54316 | |||
| 8497ffa3d4 | |||
| af02cb3fba | |||
| a01b48814a | |||
| f00c93eab3 | |||
| 516fb19076 |
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: pip
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
@@ -11,6 +11,7 @@ jobs:
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: pipx install poetry
|
||||
|
||||
@@ -17,7 +17,8 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: poetry
|
||||
- run: poetry install --with=dev
|
||||
- run: poetry run ruff --output-format=github .
|
||||
- run: poetry run ruff check --output-format=github .
|
||||
- run: poetry run ruff format --check .
|
||||
- run: poetry run pytest . --junitxml=junit/test-results-${{ matrix.python-version }}.xml --cov=ollama --cov-report=xml --cov-report=html
|
||||
- name: check poetry.lock is up-to-date
|
||||
run: poetry check --lock
|
||||
@@ -25,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
poetry export >requirements.txt
|
||||
git diff --exit-code requirements.txt
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pytest-results-${{ matrix.python-version }}
|
||||
path: junit/test-results-${{ matrix.python-version }}.xml
|
||||
|
||||
@@ -1,48 +1,137 @@
|
||||
# Ollama Python Library
|
||||
|
||||
The Ollama Python library provides the easiest way to integrate your Python 3 project with [Ollama](https://github.com/jmorganca/ollama).
|
||||
The Ollama Python library provides the easiest way to integrate Python 3.8+ projects with [Ollama](https://github.com/ollama/ollama).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Requires Python 3.8 or higher.
|
||||
## Install
|
||||
|
||||
```sh
|
||||
pip install ollama
|
||||
```
|
||||
|
||||
A global default client is provided for convenience and can be used in the same way as the synchronous client.
|
||||
## Usage
|
||||
|
||||
```python
|
||||
import ollama
|
||||
response = ollama.chat(model='llama2', messages=[{'role': 'user', 'content': 'Why is the sky blue?'}])
|
||||
response = ollama.chat(model='llama3.1', messages=[
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'Why is the sky blue?',
|
||||
},
|
||||
])
|
||||
print(response['message']['content'])
|
||||
```
|
||||
|
||||
## Streaming responses
|
||||
|
||||
Response streaming can be enabled by setting `stream=True`, modifying function calls to return a Python generator where each part is an object in the stream.
|
||||
|
||||
```python
|
||||
import ollama
|
||||
message = {'role': 'user', 'content': 'Why is the sky blue?'}
|
||||
for part in ollama.chat(model='llama2', messages=[message], stream=True):
|
||||
print(part['message']['content'], end='', flush=True)
|
||||
|
||||
stream = ollama.chat(
|
||||
model='llama3.1',
|
||||
messages=[{'role': 'user', 'content': 'Why is the sky blue?'}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
for chunk in stream:
|
||||
print(chunk['message']['content'], end='', flush=True)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
## Using the Synchronous Client
|
||||
The Ollama Python library's API is designed around the [Ollama REST API](https://github.com/ollama/ollama/blob/main/docs/api.md)
|
||||
|
||||
### Chat
|
||||
|
||||
```python
|
||||
ollama.chat(model='llama3.1', messages=[{'role': 'user', 'content': 'Why is the sky blue?'}])
|
||||
```
|
||||
|
||||
### Generate
|
||||
|
||||
```python
|
||||
ollama.generate(model='llama3.1', prompt='Why is the sky blue?')
|
||||
```
|
||||
|
||||
### List
|
||||
|
||||
```python
|
||||
ollama.list()
|
||||
```
|
||||
|
||||
### Show
|
||||
|
||||
```python
|
||||
ollama.show('llama3.1')
|
||||
```
|
||||
|
||||
### Create
|
||||
|
||||
```python
|
||||
modelfile='''
|
||||
FROM llama3.1
|
||||
SYSTEM You are mario from super mario bros.
|
||||
'''
|
||||
|
||||
ollama.create(model='example', modelfile=modelfile)
|
||||
```
|
||||
|
||||
### Copy
|
||||
|
||||
```python
|
||||
ollama.copy('llama3.1', 'user/llama3.1')
|
||||
```
|
||||
|
||||
### Delete
|
||||
|
||||
```python
|
||||
ollama.delete('llama3.1')
|
||||
```
|
||||
|
||||
### Pull
|
||||
|
||||
```python
|
||||
ollama.pull('llama3.1')
|
||||
```
|
||||
|
||||
### Push
|
||||
|
||||
```python
|
||||
ollama.push('user/llama3.1')
|
||||
```
|
||||
|
||||
### Embeddings
|
||||
|
||||
```python
|
||||
ollama.embeddings(model='llama3.1', prompt='The sky is blue because of rayleigh scattering')
|
||||
```
|
||||
|
||||
### Ps
|
||||
|
||||
```python
|
||||
ollama.ps()
|
||||
```
|
||||
|
||||
## Custom client
|
||||
|
||||
A custom client can be created with the following fields:
|
||||
|
||||
- `host`: The Ollama host to connect to
|
||||
- `timeout`: The timeout for requests
|
||||
|
||||
```python
|
||||
from ollama import Client
|
||||
message = {'role': 'user', 'content': 'Why is the sky blue?'}
|
||||
response = Client().chat(model='llama2', messages=[message])
|
||||
client = Client(host='http://localhost:11434')
|
||||
response = client.chat(model='llama3.1', messages=[
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'Why is the sky blue?',
|
||||
},
|
||||
])
|
||||
```
|
||||
|
||||
Response streaming can be enabled by setting `stream=True`. This modifies the function to return a Python generator where each part is an object in the stream.
|
||||
|
||||
```python
|
||||
from ollama import Client
|
||||
message = {'role': 'user', 'content': 'Why is the sky blue?'}
|
||||
for part in Client().chat(model='llama2', messages=[message], stream=True):
|
||||
print(part['message']['content'], end='', flush=True)
|
||||
```
|
||||
|
||||
## Using the Asynchronous Client
|
||||
## Async client
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
@@ -50,12 +139,12 @@ from ollama import AsyncClient
|
||||
|
||||
async def chat():
|
||||
message = {'role': 'user', 'content': 'Why is the sky blue?'}
|
||||
response = await AsyncClient().chat(model='llama2', messages=[message])
|
||||
response = await AsyncClient().chat(model='llama3.1', messages=[message])
|
||||
|
||||
asyncio.run(chat())
|
||||
```
|
||||
|
||||
Similar to the synchronous client, setting `stream=True` modifies the function to return a Python asynchronous generator.
|
||||
Setting `stream=True` modifies functions to return a Python asynchronous generator:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
@@ -63,13 +152,13 @@ from ollama import AsyncClient
|
||||
|
||||
async def chat():
|
||||
message = {'role': 'user', 'content': 'Why is the sky blue?'}
|
||||
async for part in await AsyncClient().chat(model='llama2', messages=[message], stream=True):
|
||||
async for part in await AsyncClient().chat(model='llama3.1', messages=[message], stream=True):
|
||||
print(part['message']['content'], end='', flush=True)
|
||||
|
||||
asyncio.run(chat())
|
||||
```
|
||||
|
||||
## Handling Errors
|
||||
## Errors
|
||||
|
||||
Errors are raised if requests return an error status or if an error is detected while streaming.
|
||||
|
||||
@@ -79,7 +168,7 @@ model = 'does-not-yet-exist'
|
||||
try:
|
||||
ollama.chat(model)
|
||||
except ollama.ResponseError as e:
|
||||
print('Error:', e.content)
|
||||
print('Error:', e.error)
|
||||
if e.status_code == 404:
|
||||
ollama.pull(model)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# async-chat-stream
|
||||
|
||||
This example demonstrates how to create a conversation history using an asynchronous Ollama client and the chat endpoint. The streaming response is outputted to `stdout` as well as a TTS if enabled with `--speak` and available. Supported TTS are `say` on macOS and `espeak` on Linux.
|
||||
@@ -0,0 +1,59 @@
|
||||
import shutil
|
||||
import asyncio
|
||||
import argparse
|
||||
|
||||
import ollama
|
||||
|
||||
|
||||
async def speak(speaker, content):
|
||||
if speaker:
|
||||
p = await asyncio.create_subprocess_exec(speaker, content)
|
||||
await p.communicate()
|
||||
|
||||
|
||||
async def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--speak', default=False, action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
speaker = None
|
||||
if not args.speak:
|
||||
...
|
||||
elif say := shutil.which('say'):
|
||||
speaker = say
|
||||
elif (espeak := shutil.which('espeak')) or (espeak := shutil.which('espeak-ng')):
|
||||
speaker = espeak
|
||||
|
||||
client = ollama.AsyncClient()
|
||||
|
||||
messages = []
|
||||
|
||||
while True:
|
||||
if content_in := input('>>> '):
|
||||
messages.append({'role': 'user', 'content': content_in})
|
||||
|
||||
content_out = ''
|
||||
message = {'role': 'assistant', 'content': ''}
|
||||
async for response in await client.chat(model='mistral', messages=messages, stream=True):
|
||||
if response['done']:
|
||||
messages.append(message)
|
||||
|
||||
content = response['message']['content']
|
||||
print(content, end='', flush=True)
|
||||
|
||||
content_out += content
|
||||
if content in ['.', '!', '?', '\n']:
|
||||
await speak(speaker, content_out)
|
||||
content_out = ''
|
||||
|
||||
message['content'] += content
|
||||
|
||||
if content_out:
|
||||
await speak(speaker, content_out)
|
||||
print()
|
||||
|
||||
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
...
|
||||
@@ -0,0 +1,20 @@
|
||||
import sys
|
||||
|
||||
from ollama import create
|
||||
|
||||
|
||||
args = sys.argv[1:]
|
||||
if len(args) == 2:
|
||||
# create from local file
|
||||
path = args[1]
|
||||
else:
|
||||
print('usage: python main.py <name> <filepath>')
|
||||
sys.exit(1)
|
||||
|
||||
# TODO: update to real Modelfile values
|
||||
modelfile = f"""
|
||||
FROM {path}
|
||||
"""
|
||||
|
||||
for response in create(model=args[0], modelfile=modelfile, stream=True):
|
||||
print(response['status'])
|
||||
@@ -1,16 +1,16 @@
|
||||
from ollama import generate
|
||||
|
||||
prefix = '''def remove_non_ascii(s: str) -> str:
|
||||
prompt = '''def remove_non_ascii(s: str) -> str:
|
||||
""" '''
|
||||
|
||||
suffix = """
|
||||
return result
|
||||
"""
|
||||
|
||||
|
||||
response = generate(
|
||||
model='codellama:7b-code',
|
||||
prompt=f'<PRE> {prefix} <SUF>{suffix} <MID>',
|
||||
prompt=prompt,
|
||||
suffix=suffix,
|
||||
options={
|
||||
'num_predict': 128,
|
||||
'temperature': 0,
|
||||
@@ -0,0 +1,31 @@
|
||||
from ollama import ps, pull, chat
|
||||
|
||||
response = pull('mistral', stream=True)
|
||||
progress_states = set()
|
||||
for progress in response:
|
||||
if progress.get('status') in progress_states:
|
||||
continue
|
||||
progress_states.add(progress.get('status'))
|
||||
print(progress.get('status'))
|
||||
|
||||
print('\n')
|
||||
|
||||
response = chat('mistral', messages=[{'role': 'user', 'content': 'Hello!'}])
|
||||
print(response['message']['content'])
|
||||
|
||||
print('\n')
|
||||
|
||||
response = ps()
|
||||
|
||||
name = response['models'][0]['name']
|
||||
size = response['models'][0]['size']
|
||||
size_vram = response['models'][0]['size_vram']
|
||||
|
||||
if size == size_vram:
|
||||
print(f'{name}: 100% GPU')
|
||||
elif not size_vram:
|
||||
print(f'{name}: 100% CPU')
|
||||
else:
|
||||
size_cpu = size - size_vram
|
||||
cpu_percent = round(size_cpu / size * 100)
|
||||
print(f'{name}: {cpu_percent}% CPU/{100 - cpu_percent}% GPU')
|
||||
@@ -0,0 +1,9 @@
|
||||
# pull-progress
|
||||
|
||||
This example emulates `ollama pull` using the Python library and [`tqdm`](https://tqdm.github.io/).
|
||||
|
||||
## Setup
|
||||
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
from tqdm import tqdm
|
||||
from ollama import pull
|
||||
|
||||
|
||||
current_digest, bars = '', {}
|
||||
for progress in pull('mistral', stream=True):
|
||||
digest = progress.get('digest', '')
|
||||
if digest != current_digest and current_digest in bars:
|
||||
bars[current_digest].close()
|
||||
|
||||
if not digest:
|
||||
print(progress.get('status'))
|
||||
continue
|
||||
|
||||
if digest not in bars and (total := progress.get('total')):
|
||||
bars[digest] = tqdm(total=total, desc=f'pulling {digest[7:19]}', unit='B', unit_scale=True)
|
||||
|
||||
if completed := progress.get('completed'):
|
||||
bars[digest].update(completed - bars[digest].n)
|
||||
|
||||
current_digest = digest
|
||||
@@ -0,0 +1 @@
|
||||
tqdm==4.66.1
|
||||
@@ -0,0 +1,3 @@
|
||||
# tools
|
||||
|
||||
This example demonstrates how to utilize tool calls with an asynchronous Ollama client and the chat endpoint.
|
||||
@@ -0,0 +1,87 @@
|
||||
import json
|
||||
import ollama
|
||||
import asyncio
|
||||
|
||||
|
||||
# Simulates an API call to get flight times
|
||||
# In a real application, this would fetch data from a live database or API
|
||||
def get_flight_times(departure: str, arrival: str) -> str:
|
||||
flights = {
|
||||
'NYC-LAX': {'departure': '08:00 AM', 'arrival': '11:30 AM', 'duration': '5h 30m'},
|
||||
'LAX-NYC': {'departure': '02:00 PM', 'arrival': '10:30 PM', 'duration': '5h 30m'},
|
||||
'LHR-JFK': {'departure': '10:00 AM', 'arrival': '01:00 PM', 'duration': '8h 00m'},
|
||||
'JFK-LHR': {'departure': '09:00 PM', 'arrival': '09:00 AM', 'duration': '7h 00m'},
|
||||
'CDG-DXB': {'departure': '11:00 AM', 'arrival': '08:00 PM', 'duration': '6h 00m'},
|
||||
'DXB-CDG': {'departure': '03:00 AM', 'arrival': '07:30 AM', 'duration': '7h 30m'},
|
||||
}
|
||||
|
||||
key = f'{departure}-{arrival}'.upper()
|
||||
return json.dumps(flights.get(key, {'error': 'Flight not found'}))
|
||||
|
||||
|
||||
async def run(model: str):
|
||||
client = ollama.AsyncClient()
|
||||
# Initialize conversation with a user query
|
||||
messages = [{'role': 'user', 'content': 'What is the flight time from New York (NYC) to Los Angeles (LAX)?'}]
|
||||
|
||||
# First API call: Send the query and function description to the model
|
||||
response = await client.chat(
|
||||
model=model,
|
||||
messages=messages,
|
||||
tools=[
|
||||
{
|
||||
'type': 'function',
|
||||
'function': {
|
||||
'name': 'get_flight_times',
|
||||
'description': 'Get the flight times between two cities',
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'departure': {
|
||||
'type': 'string',
|
||||
'description': 'The departure city (airport code)',
|
||||
},
|
||||
'arrival': {
|
||||
'type': 'string',
|
||||
'description': 'The arrival city (airport code)',
|
||||
},
|
||||
},
|
||||
'required': ['departure', 'arrival'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
# Add the model's response to the conversation history
|
||||
messages.append(response['message'])
|
||||
|
||||
# Check if the model decided to use the provided function
|
||||
if not response['message'].get('tool_calls'):
|
||||
print("The model didn't use the function. Its response was:")
|
||||
print(response['message']['content'])
|
||||
return
|
||||
|
||||
# Process function calls made by the model
|
||||
if response['message'].get('tool_calls'):
|
||||
available_functions = {
|
||||
'get_flight_times': get_flight_times,
|
||||
}
|
||||
for tool in response['message']['tool_calls']:
|
||||
function_to_call = available_functions[tool['function']['name']]
|
||||
function_response = function_to_call(tool['function']['arguments']['departure'], tool['function']['arguments']['arrival'])
|
||||
# Add function response to the conversation
|
||||
messages.append(
|
||||
{
|
||||
'role': 'tool',
|
||||
'content': function_response,
|
||||
}
|
||||
)
|
||||
|
||||
# Second API call: Get final response from the model
|
||||
final_response = await client.chat(model=model, messages=messages)
|
||||
print(final_response['message']['content'])
|
||||
|
||||
|
||||
# Run the async function
|
||||
asyncio.run(run('mistral'))
|
||||
@@ -21,6 +21,7 @@ __all__ = [
|
||||
'ResponseError',
|
||||
'generate',
|
||||
'chat',
|
||||
'embed',
|
||||
'embeddings',
|
||||
'pull',
|
||||
'push',
|
||||
@@ -29,12 +30,14 @@ __all__ = [
|
||||
'list',
|
||||
'copy',
|
||||
'show',
|
||||
'ps',
|
||||
]
|
||||
|
||||
_client = Client()
|
||||
|
||||
generate = _client.generate
|
||||
chat = _client.chat
|
||||
embed = _client.embed
|
||||
embeddings = _client.embeddings
|
||||
pull = _client.pull
|
||||
push = _client.push
|
||||
@@ -43,3 +46,4 @@ delete = _client.delete
|
||||
list = _client.list
|
||||
copy = _client.copy
|
||||
show = _client.show
|
||||
ps = _client.ps
|
||||
|
||||
+483
-46
@@ -1,13 +1,18 @@
|
||||
import ipaddress
|
||||
import os
|
||||
import io
|
||||
import json
|
||||
import httpx
|
||||
import binascii
|
||||
import platform
|
||||
import urllib.parse
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from copy import deepcopy
|
||||
from hashlib import sha256
|
||||
from base64 import b64encode
|
||||
from base64 import b64encode, b64decode
|
||||
|
||||
from typing import Any, AnyStr, Union, Optional, Sequence, Mapping, Literal
|
||||
from typing import Any, AnyStr, Union, Optional, Sequence, Mapping, Literal, overload
|
||||
|
||||
import sys
|
||||
|
||||
@@ -16,14 +21,21 @@ if sys.version_info < (3, 9):
|
||||
else:
|
||||
from collections.abc import Iterator, AsyncIterator
|
||||
|
||||
from ollama._types import Message, Options, RequestError, ResponseError
|
||||
from importlib import metadata
|
||||
|
||||
try:
|
||||
__version__ = metadata.version('ollama')
|
||||
except metadata.PackageNotFoundError:
|
||||
__version__ = '0.0.0'
|
||||
|
||||
from ollama._types import Message, Options, RequestError, ResponseError, Tool
|
||||
|
||||
|
||||
class BaseClient:
|
||||
def __init__(
|
||||
self,
|
||||
client,
|
||||
base_url: Optional[str] = None,
|
||||
host: Optional[str] = None,
|
||||
follow_redirects: bool = True,
|
||||
timeout: Any = None,
|
||||
**kwargs,
|
||||
@@ -31,24 +43,28 @@ class BaseClient:
|
||||
"""
|
||||
Creates a httpx client. Default parameters are the same as those defined in httpx
|
||||
except for the following:
|
||||
|
||||
- `base_url`: http://127.0.0.1:11434
|
||||
- `follow_redirects`: True
|
||||
- `timeout`: None
|
||||
|
||||
`kwargs` are passed to the httpx client.
|
||||
"""
|
||||
|
||||
headers = kwargs.pop('headers', {})
|
||||
headers['Content-Type'] = 'application/json'
|
||||
headers['Accept'] = 'application/json'
|
||||
headers['User-Agent'] = f'ollama-python/{__version__} ({platform.machine()} {platform.system().lower()}) Python/{platform.python_version()}'
|
||||
|
||||
self._client = client(
|
||||
base_url=base_url or os.getenv('OLLAMA_HOST', 'http://127.0.0.1:11434'),
|
||||
base_url=_parse_host(host or os.getenv('OLLAMA_HOST')),
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
headers=headers,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class Client(BaseClient):
|
||||
def __init__(self, base_url: Optional[str] = None, **kwargs) -> None:
|
||||
super().__init__(httpx.Client, base_url, **kwargs)
|
||||
def __init__(self, host: Optional[str] = None, **kwargs) -> None:
|
||||
super().__init__(httpx.Client, host, **kwargs)
|
||||
|
||||
def _request(self, method: str, url: str, **kwargs) -> httpx.Response:
|
||||
response = self._client.request(method, url, **kwargs)
|
||||
@@ -82,10 +98,45 @@ class Client(BaseClient):
|
||||
) -> Union[Mapping[str, Any], Iterator[Mapping[str, Any]]]:
|
||||
return self._stream(*args, **kwargs) if stream else self._request(*args, **kwargs).json()
|
||||
|
||||
@overload
|
||||
def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
stream: Literal[False] = False,
|
||||
raw: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
stream: Literal[True] = True,
|
||||
raw: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Iterator[Mapping[str, Any]]: ...
|
||||
|
||||
def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
@@ -94,6 +145,7 @@ class Client(BaseClient):
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Union[Mapping[str, Any], Iterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
Create a response using the requested model.
|
||||
@@ -114,6 +166,7 @@ class Client(BaseClient):
|
||||
json={
|
||||
'model': model,
|
||||
'prompt': prompt,
|
||||
'suffix': suffix,
|
||||
'system': system,
|
||||
'template': template,
|
||||
'context': context or [],
|
||||
@@ -122,17 +175,44 @@ class Client(BaseClient):
|
||||
'images': [_encode_image(image) for image in images or []],
|
||||
'format': format,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: Literal[False] = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: Literal[True] = True,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Iterator[Mapping[str, Any]]: ...
|
||||
|
||||
def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Union[Mapping[str, Any], Iterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
Create a chat response using the requested model.
|
||||
@@ -147,13 +227,9 @@ class Client(BaseClient):
|
||||
if not model:
|
||||
raise RequestError('must provide a model')
|
||||
|
||||
messages = deepcopy(messages)
|
||||
|
||||
for message in messages or []:
|
||||
if not isinstance(message, dict):
|
||||
raise TypeError('messages must be a list of Message or dict-like objects')
|
||||
if not (role := message.get('role')) or role not in ['system', 'user', 'assistant']:
|
||||
raise RequestError('messages must contain a role and it must be one of "system", "user", or "assistant"')
|
||||
if not message.get('content'):
|
||||
raise RequestError('messages must contain content')
|
||||
if images := message.get('images'):
|
||||
message['images'] = [_encode_image(image) for image in images]
|
||||
|
||||
@@ -163,14 +239,45 @@ class Client(BaseClient):
|
||||
json={
|
||||
'model': model,
|
||||
'messages': messages,
|
||||
'tools': tools or [],
|
||||
'stream': stream,
|
||||
'format': format,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
def embeddings(self, model: str = '', prompt: str = '', options: Optional[Options] = None) -> Sequence[float]:
|
||||
def embed(
|
||||
self,
|
||||
model: str = '',
|
||||
input: Union[str, Sequence[AnyStr]] = '',
|
||||
truncate: bool = True,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]:
|
||||
if not model:
|
||||
raise RequestError('must provide a model')
|
||||
|
||||
return self._request(
|
||||
'POST',
|
||||
'/api/embed',
|
||||
json={
|
||||
'model': model,
|
||||
'input': input,
|
||||
'truncate': truncate,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
).json()
|
||||
|
||||
def embeddings(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Sequence[float]]:
|
||||
return self._request(
|
||||
'POST',
|
||||
'/api/embeddings',
|
||||
@@ -178,9 +285,26 @@ class Client(BaseClient):
|
||||
'model': model,
|
||||
'prompt': prompt,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
).json()
|
||||
|
||||
@overload
|
||||
def pull(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
def pull(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[True] = True,
|
||||
) -> Iterator[Mapping[str, Any]]: ...
|
||||
|
||||
def pull(
|
||||
self,
|
||||
model: str,
|
||||
@@ -203,6 +327,22 @@ class Client(BaseClient):
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
def push(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
def push(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[True] = True,
|
||||
) -> Iterator[Mapping[str, Any]]: ...
|
||||
|
||||
def push(
|
||||
self,
|
||||
model: str,
|
||||
@@ -225,11 +365,32 @@ class Client(BaseClient):
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: Literal[True] = True,
|
||||
) -> Iterator[Mapping[str, Any]]: ...
|
||||
|
||||
def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
) -> Union[Mapping[str, Any], Iterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
@@ -251,6 +412,7 @@ class Client(BaseClient):
|
||||
'name': model,
|
||||
'modelfile': modelfile,
|
||||
'stream': stream,
|
||||
'quantize': quantize,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
@@ -261,13 +423,16 @@ class Client(BaseClient):
|
||||
out = io.StringIO()
|
||||
for line in io.StringIO(modelfile):
|
||||
command, _, args = line.partition(' ')
|
||||
if command.upper() in ['FROM', 'ADAPTER']:
|
||||
path = Path(args.strip()).expanduser()
|
||||
path = path if path.is_absolute() else base / path
|
||||
if path.exists():
|
||||
args = f'@{self._create_blob(path)}'
|
||||
if command.upper() not in ['FROM', 'ADAPTER']:
|
||||
print(line, end='', file=out)
|
||||
continue
|
||||
|
||||
path = Path(args.strip()).expanduser()
|
||||
path = path if path.is_absolute() else base / path
|
||||
if path.exists():
|
||||
args = f'@{self._create_blob(path)}\n'
|
||||
print(command, args, end='', file=out)
|
||||
|
||||
print(command, args, file=out)
|
||||
return out.getvalue()
|
||||
|
||||
def _create_blob(self, path: Union[str, Path]) -> str:
|
||||
@@ -306,10 +471,13 @@ class Client(BaseClient):
|
||||
def show(self, model: str) -> Mapping[str, Any]:
|
||||
return self._request('POST', '/api/show', json={'name': model}).json()
|
||||
|
||||
def ps(self) -> Mapping[str, Any]:
|
||||
return self._request('GET', '/api/ps').json()
|
||||
|
||||
|
||||
class AsyncClient(BaseClient):
|
||||
def __init__(self, base_url: Optional[str] = None, **kwargs) -> None:
|
||||
super().__init__(httpx.AsyncClient, base_url, **kwargs)
|
||||
def __init__(self, host: Optional[str] = None, **kwargs) -> None:
|
||||
super().__init__(httpx.AsyncClient, host, **kwargs)
|
||||
|
||||
async def _request(self, method: str, url: str, **kwargs) -> httpx.Response:
|
||||
response = await self._client.request(method, url, **kwargs)
|
||||
@@ -350,10 +518,45 @@ class AsyncClient(BaseClient):
|
||||
response = await self._request(*args, **kwargs)
|
||||
return response.json()
|
||||
|
||||
@overload
|
||||
async def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
stream: Literal[False] = False,
|
||||
raw: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
async def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
stream: Literal[True] = True,
|
||||
raw: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> AsyncIterator[Mapping[str, Any]]: ...
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
suffix: str = '',
|
||||
system: str = '',
|
||||
template: str = '',
|
||||
context: Optional[Sequence[int]] = None,
|
||||
@@ -362,6 +565,7 @@ class AsyncClient(BaseClient):
|
||||
format: Literal['', 'json'] = '',
|
||||
images: Optional[Sequence[AnyStr]] = None,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Union[Mapping[str, Any], AsyncIterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
Create a response using the requested model.
|
||||
@@ -381,6 +585,7 @@ class AsyncClient(BaseClient):
|
||||
json={
|
||||
'model': model,
|
||||
'prompt': prompt,
|
||||
'suffix': suffix,
|
||||
'system': system,
|
||||
'template': template,
|
||||
'context': context or [],
|
||||
@@ -389,17 +594,44 @@ class AsyncClient(BaseClient):
|
||||
'images': [_encode_image(image) for image in images or []],
|
||||
'format': format,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
async def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: Literal[False] = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
async def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: Literal[True] = True,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> AsyncIterator[Mapping[str, Any]]: ...
|
||||
|
||||
async def chat(
|
||||
self,
|
||||
model: str = '',
|
||||
messages: Optional[Sequence[Message]] = None,
|
||||
tools: Optional[Sequence[Tool]] = None,
|
||||
stream: bool = False,
|
||||
format: Literal['', 'json'] = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Union[Mapping[str, Any], AsyncIterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
Create a chat response using the requested model.
|
||||
@@ -413,13 +645,9 @@ class AsyncClient(BaseClient):
|
||||
if not model:
|
||||
raise RequestError('must provide a model')
|
||||
|
||||
messages = deepcopy(messages)
|
||||
|
||||
for message in messages or []:
|
||||
if not isinstance(message, dict):
|
||||
raise TypeError('messages must be a list of strings')
|
||||
if not (role := message.get('role')) or role not in ['system', 'user', 'assistant']:
|
||||
raise RequestError('messages must contain a role and it must be one of "system", "user", or "assistant"')
|
||||
if not message.get('content'):
|
||||
raise RequestError('messages must contain content')
|
||||
if images := message.get('images'):
|
||||
message['images'] = [_encode_image(image) for image in images]
|
||||
|
||||
@@ -429,14 +657,47 @@ class AsyncClient(BaseClient):
|
||||
json={
|
||||
'model': model,
|
||||
'messages': messages,
|
||||
'tools': tools or [],
|
||||
'stream': stream,
|
||||
'format': format,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
async def embeddings(self, model: str = '', prompt: str = '', options: Optional[Options] = None) -> Sequence[float]:
|
||||
async def embed(
|
||||
self,
|
||||
model: str = '',
|
||||
input: Union[str, Sequence[AnyStr]] = '',
|
||||
truncate: bool = True,
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Any]:
|
||||
if not model:
|
||||
raise RequestError('must provide a model')
|
||||
|
||||
response = await self._request(
|
||||
'POST',
|
||||
'/api/embed',
|
||||
json={
|
||||
'model': model,
|
||||
'input': input,
|
||||
'truncate': truncate,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
async def embeddings(
|
||||
self,
|
||||
model: str = '',
|
||||
prompt: str = '',
|
||||
options: Optional[Options] = None,
|
||||
keep_alive: Optional[Union[float, str]] = None,
|
||||
) -> Mapping[str, Sequence[float]]:
|
||||
response = await self._request(
|
||||
'POST',
|
||||
'/api/embeddings',
|
||||
@@ -444,11 +705,28 @@ class AsyncClient(BaseClient):
|
||||
'model': model,
|
||||
'prompt': prompt,
|
||||
'options': options or {},
|
||||
'keep_alive': keep_alive,
|
||||
},
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
@overload
|
||||
async def pull(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
async def pull(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[True] = True,
|
||||
) -> AsyncIterator[Mapping[str, Any]]: ...
|
||||
|
||||
async def pull(
|
||||
self,
|
||||
model: str,
|
||||
@@ -471,6 +749,22 @@ class AsyncClient(BaseClient):
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
async def push(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
async def push(
|
||||
self,
|
||||
model: str,
|
||||
insecure: bool = False,
|
||||
stream: Literal[True] = True,
|
||||
) -> AsyncIterator[Mapping[str, Any]]: ...
|
||||
|
||||
async def push(
|
||||
self,
|
||||
model: str,
|
||||
@@ -493,11 +787,32 @@ class AsyncClient(BaseClient):
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@overload
|
||||
async def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: Literal[False] = False,
|
||||
) -> Mapping[str, Any]: ...
|
||||
|
||||
@overload
|
||||
async def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: Literal[True] = True,
|
||||
) -> AsyncIterator[Mapping[str, Any]]: ...
|
||||
|
||||
async def create(
|
||||
self,
|
||||
model: str,
|
||||
path: Optional[Union[str, PathLike]] = None,
|
||||
modelfile: Optional[str] = None,
|
||||
quantize: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
) -> Union[Mapping[str, Any], AsyncIterator[Mapping[str, Any]]]:
|
||||
"""
|
||||
@@ -519,6 +834,7 @@ class AsyncClient(BaseClient):
|
||||
'name': model,
|
||||
'modelfile': modelfile,
|
||||
'stream': stream,
|
||||
'quantize': quantize,
|
||||
},
|
||||
stream=stream,
|
||||
)
|
||||
@@ -529,13 +845,16 @@ class AsyncClient(BaseClient):
|
||||
out = io.StringIO()
|
||||
for line in io.StringIO(modelfile):
|
||||
command, _, args = line.partition(' ')
|
||||
if command.upper() in ['FROM', 'ADAPTER']:
|
||||
path = Path(args).expanduser()
|
||||
path = path if path.is_absolute() else base / path
|
||||
if path.exists():
|
||||
args = f'@{await self._create_blob(path)}'
|
||||
if command.upper() not in ['FROM', 'ADAPTER']:
|
||||
print(line, end='', file=out)
|
||||
continue
|
||||
|
||||
path = Path(args.strip()).expanduser()
|
||||
path = path if path.is_absolute() else base / path
|
||||
if path.exists():
|
||||
args = f'@{await self._create_blob(path)}\n'
|
||||
print(command, args, end='', file=out)
|
||||
|
||||
print(command, args, file=out)
|
||||
return out.getvalue()
|
||||
|
||||
async def _create_blob(self, path: Union[str, Path]) -> str:
|
||||
@@ -583,21 +902,49 @@ class AsyncClient(BaseClient):
|
||||
response = await self._request('POST', '/api/show', json={'name': model})
|
||||
return response.json()
|
||||
|
||||
async def ps(self) -> Mapping[str, Any]:
|
||||
response = await self._request('GET', '/api/ps')
|
||||
return response.json()
|
||||
|
||||
|
||||
def _encode_image(image) -> str:
|
||||
if p := _as_path(image):
|
||||
b64 = b64encode(p.read_bytes())
|
||||
elif b := _as_bytesio(image):
|
||||
b64 = b64encode(b.read())
|
||||
else:
|
||||
raise RequestError('images must be a list of bytes, path-like objects, or file-like objects')
|
||||
"""
|
||||
>>> _encode_image(b'ollama')
|
||||
'b2xsYW1h'
|
||||
>>> _encode_image(io.BytesIO(b'ollama'))
|
||||
'b2xsYW1h'
|
||||
>>> _encode_image('LICENSE')
|
||||
'TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgT2xsYW1hCgpQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5Cm9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlICJTb2Z0d2FyZSIpLCB0byBkZWFsCmluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMKdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLCBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbApjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMKZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKClRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluIGFsbApjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgoKVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1IKSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksCkZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSCkxJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sCk9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFClNPRlRXQVJFLgo='
|
||||
>>> _encode_image(Path('LICENSE'))
|
||||
'TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgT2xsYW1hCgpQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5Cm9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlICJTb2Z0d2FyZSIpLCB0byBkZWFsCmluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMKdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLCBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbApjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMKZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKClRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluIGFsbApjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgoKVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1IKSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksCkZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSCkxJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sCk9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFClNPRlRXQVJFLgo='
|
||||
>>> _encode_image('YWJj')
|
||||
'YWJj'
|
||||
>>> _encode_image(b'YWJj')
|
||||
'YWJj'
|
||||
"""
|
||||
|
||||
return b64.decode('utf-8')
|
||||
if p := _as_path(image):
|
||||
return b64encode(p.read_bytes()).decode('utf-8')
|
||||
|
||||
try:
|
||||
b64decode(image, validate=True)
|
||||
return image if isinstance(image, str) else image.decode('utf-8')
|
||||
except (binascii.Error, TypeError):
|
||||
...
|
||||
|
||||
if b := _as_bytesio(image):
|
||||
return b64encode(b.read()).decode('utf-8')
|
||||
|
||||
raise RequestError('image must be bytes, path-like object, or file-like object')
|
||||
|
||||
|
||||
def _as_path(s: Optional[Union[str, PathLike]]) -> Union[Path, None]:
|
||||
if isinstance(s, str) or isinstance(s, Path):
|
||||
return Path(s)
|
||||
try:
|
||||
if (p := Path(s)).exists():
|
||||
return p
|
||||
except Exception:
|
||||
...
|
||||
return None
|
||||
|
||||
|
||||
@@ -607,3 +954,93 @@ def _as_bytesio(s: Any) -> Union[io.BytesIO, None]:
|
||||
elif isinstance(s, bytes):
|
||||
return io.BytesIO(s)
|
||||
return None
|
||||
|
||||
|
||||
def _parse_host(host: Optional[str]) -> str:
|
||||
"""
|
||||
>>> _parse_host(None)
|
||||
'http://127.0.0.1:11434'
|
||||
>>> _parse_host('')
|
||||
'http://127.0.0.1:11434'
|
||||
>>> _parse_host('1.2.3.4')
|
||||
'http://1.2.3.4:11434'
|
||||
>>> _parse_host(':56789')
|
||||
'http://127.0.0.1:56789'
|
||||
>>> _parse_host('1.2.3.4:56789')
|
||||
'http://1.2.3.4:56789'
|
||||
>>> _parse_host('http://1.2.3.4')
|
||||
'http://1.2.3.4:80'
|
||||
>>> _parse_host('https://1.2.3.4')
|
||||
'https://1.2.3.4:443'
|
||||
>>> _parse_host('https://1.2.3.4:56789')
|
||||
'https://1.2.3.4:56789'
|
||||
>>> _parse_host('example.com')
|
||||
'http://example.com:11434'
|
||||
>>> _parse_host('example.com:56789')
|
||||
'http://example.com:56789'
|
||||
>>> _parse_host('http://example.com')
|
||||
'http://example.com:80'
|
||||
>>> _parse_host('https://example.com')
|
||||
'https://example.com:443'
|
||||
>>> _parse_host('https://example.com:56789')
|
||||
'https://example.com:56789'
|
||||
>>> _parse_host('example.com/')
|
||||
'http://example.com:11434'
|
||||
>>> _parse_host('example.com:56789/')
|
||||
'http://example.com:56789'
|
||||
>>> _parse_host('example.com/path')
|
||||
'http://example.com:11434/path'
|
||||
>>> _parse_host('example.com:56789/path')
|
||||
'http://example.com:56789/path'
|
||||
>>> _parse_host('https://example.com:56789/path')
|
||||
'https://example.com:56789/path'
|
||||
>>> _parse_host('example.com:56789/path/')
|
||||
'http://example.com:56789/path'
|
||||
>>> _parse_host('[0001:002:003:0004::1]')
|
||||
'http://[0001:002:003:0004::1]:11434'
|
||||
>>> _parse_host('[0001:002:003:0004::1]:56789')
|
||||
'http://[0001:002:003:0004::1]:56789'
|
||||
>>> _parse_host('http://[0001:002:003:0004::1]')
|
||||
'http://[0001:002:003:0004::1]:80'
|
||||
>>> _parse_host('https://[0001:002:003:0004::1]')
|
||||
'https://[0001:002:003:0004::1]:443'
|
||||
>>> _parse_host('https://[0001:002:003:0004::1]:56789')
|
||||
'https://[0001:002:003:0004::1]:56789'
|
||||
>>> _parse_host('[0001:002:003:0004::1]/')
|
||||
'http://[0001:002:003:0004::1]:11434'
|
||||
>>> _parse_host('[0001:002:003:0004::1]:56789/')
|
||||
'http://[0001:002:003:0004::1]:56789'
|
||||
>>> _parse_host('[0001:002:003:0004::1]/path')
|
||||
'http://[0001:002:003:0004::1]:11434/path'
|
||||
>>> _parse_host('[0001:002:003:0004::1]:56789/path')
|
||||
'http://[0001:002:003:0004::1]:56789/path'
|
||||
>>> _parse_host('https://[0001:002:003:0004::1]:56789/path')
|
||||
'https://[0001:002:003:0004::1]:56789/path'
|
||||
>>> _parse_host('[0001:002:003:0004::1]:56789/path/')
|
||||
'http://[0001:002:003:0004::1]:56789/path'
|
||||
"""
|
||||
|
||||
host, port = host or '', 11434
|
||||
scheme, _, hostport = host.partition('://')
|
||||
if not hostport:
|
||||
scheme, hostport = 'http', host
|
||||
elif scheme == 'http':
|
||||
port = 80
|
||||
elif scheme == 'https':
|
||||
port = 443
|
||||
|
||||
split = urllib.parse.urlsplit('://'.join([scheme, hostport]))
|
||||
host = split.hostname or '127.0.0.1'
|
||||
port = split.port or port
|
||||
|
||||
# Fix missing square brackets for IPv6 from urlsplit
|
||||
try:
|
||||
if isinstance(ipaddress.ip_address(host), ipaddress.IPv6Address):
|
||||
host = f'[{host}]'
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
if path := split.path.strip('/'):
|
||||
return f'{scheme}://{host}:{port}/{path}'
|
||||
|
||||
return f'{scheme}://{host}:{port}'
|
||||
|
||||
+79
-30
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
from typing import Any, TypedDict, Sequence, Literal
|
||||
from typing import Any, TypedDict, Sequence, Literal, Mapping
|
||||
|
||||
import sys
|
||||
|
||||
@@ -11,31 +11,34 @@ else:
|
||||
|
||||
class BaseGenerateResponse(TypedDict):
|
||||
model: str
|
||||
"Model used to generate response."
|
||||
'Model used to generate response.'
|
||||
|
||||
created_at: str
|
||||
"Time when the request was created."
|
||||
'Time when the request was created.'
|
||||
|
||||
done: bool
|
||||
"True if response is complete, otherwise False. Useful for streaming to detect the final response."
|
||||
'True if response is complete, otherwise False. Useful for streaming to detect the final response.'
|
||||
|
||||
done_reason: str
|
||||
'Reason for completion. Only present when done is True.'
|
||||
|
||||
total_duration: int
|
||||
"Total duration in nanoseconds."
|
||||
'Total duration in nanoseconds.'
|
||||
|
||||
load_duration: int
|
||||
"Load duration in nanoseconds."
|
||||
'Load duration in nanoseconds.'
|
||||
|
||||
prompt_eval_count: int
|
||||
"Number of tokens evaluated in the prompt."
|
||||
'Number of tokens evaluated in the prompt.'
|
||||
|
||||
prompt_eval_duration: int
|
||||
"Duration of evaluating the prompt in nanoseconds."
|
||||
'Duration of evaluating the prompt in nanoseconds.'
|
||||
|
||||
eval_count: int
|
||||
"Number of tokens evaluated in inference."
|
||||
'Number of tokens evaluated in inference.'
|
||||
|
||||
eval_duration: int
|
||||
"Duration of evaluating inference in nanoseconds."
|
||||
'Duration of evaluating inference in nanoseconds.'
|
||||
|
||||
|
||||
class GenerateResponse(BaseGenerateResponse):
|
||||
@@ -44,10 +47,31 @@ class GenerateResponse(BaseGenerateResponse):
|
||||
"""
|
||||
|
||||
response: str
|
||||
"Response content. When streaming, this contains a fragment of the response."
|
||||
'Response content. When streaming, this contains a fragment of the response.'
|
||||
|
||||
context: Sequence[int]
|
||||
"Tokenized history up to the point of the response."
|
||||
'Tokenized history up to the point of the response.'
|
||||
|
||||
|
||||
class ToolCallFunction(TypedDict):
|
||||
"""
|
||||
Tool call function.
|
||||
"""
|
||||
|
||||
name: str
|
||||
'Name of the function.'
|
||||
|
||||
arguments: NotRequired[Mapping[str, Any]]
|
||||
'Arguments of the function.'
|
||||
|
||||
|
||||
class ToolCall(TypedDict):
|
||||
"""
|
||||
Model tool calls.
|
||||
"""
|
||||
|
||||
function: ToolCallFunction
|
||||
'Function to be called.'
|
||||
|
||||
|
||||
class Message(TypedDict):
|
||||
@@ -55,11 +79,11 @@ class Message(TypedDict):
|
||||
Chat message.
|
||||
"""
|
||||
|
||||
role: Literal['user', 'assistant', 'system']
|
||||
"Assumed role of the message. Response messages always has role 'assistant'."
|
||||
role: Literal['user', 'assistant', 'system', 'tool']
|
||||
"Assumed role of the message. Response messages always has role 'assistant' or 'tool'."
|
||||
|
||||
content: str
|
||||
"Content of the message. Response messages contains message fragments when streaming."
|
||||
content: NotRequired[str]
|
||||
'Content of the message. Response messages contains message fragments when streaming.'
|
||||
|
||||
images: NotRequired[Sequence[Any]]
|
||||
"""
|
||||
@@ -73,6 +97,34 @@ class Message(TypedDict):
|
||||
Valid image formats depend on the model. See the model card for more information.
|
||||
"""
|
||||
|
||||
tool_calls: NotRequired[Sequence[ToolCall]]
|
||||
"""
|
||||
Tools calls to be made by the model.
|
||||
"""
|
||||
|
||||
|
||||
class Property(TypedDict):
|
||||
type: str
|
||||
description: str
|
||||
enum: NotRequired[Sequence[str]] # `enum` is optional and can be a list of strings
|
||||
|
||||
|
||||
class Parameters(TypedDict):
|
||||
type: str
|
||||
required: Sequence[str]
|
||||
properties: Mapping[str, Property]
|
||||
|
||||
|
||||
class ToolFunction(TypedDict):
|
||||
name: str
|
||||
description: str
|
||||
parameters: Parameters
|
||||
|
||||
|
||||
class Tool(TypedDict):
|
||||
type: str
|
||||
function: ToolFunction
|
||||
|
||||
|
||||
class ChatResponse(BaseGenerateResponse):
|
||||
"""
|
||||
@@ -80,7 +132,7 @@ class ChatResponse(BaseGenerateResponse):
|
||||
"""
|
||||
|
||||
message: Message
|
||||
"Response message."
|
||||
'Response message.'
|
||||
|
||||
|
||||
class ProgressResponse(TypedDict):
|
||||
@@ -95,7 +147,6 @@ class Options(TypedDict, total=False):
|
||||
numa: bool
|
||||
num_ctx: int
|
||||
num_batch: int
|
||||
num_gqa: int
|
||||
num_gpu: int
|
||||
main_gpu: int
|
||||
low_vram: bool
|
||||
@@ -105,8 +156,6 @@ class Options(TypedDict, total=False):
|
||||
use_mmap: bool
|
||||
use_mlock: bool
|
||||
embedding_only: bool
|
||||
rope_frequency_base: float
|
||||
rope_frequency_scale: float
|
||||
num_thread: int
|
||||
|
||||
# runtime options
|
||||
@@ -134,10 +183,10 @@ class RequestError(Exception):
|
||||
Common class for request errors.
|
||||
"""
|
||||
|
||||
def __init__(self, content: str):
|
||||
super().__init__(content)
|
||||
self.content = content
|
||||
"Reason for the error."
|
||||
def __init__(self, error: str):
|
||||
super().__init__(error)
|
||||
self.error = error
|
||||
'Reason for the error.'
|
||||
|
||||
|
||||
class ResponseError(Exception):
|
||||
@@ -145,17 +194,17 @@ class ResponseError(Exception):
|
||||
Common class for response errors.
|
||||
"""
|
||||
|
||||
def __init__(self, content: str, status_code: int = -1):
|
||||
def __init__(self, error: str, status_code: int = -1):
|
||||
try:
|
||||
# try to parse content as JSON and extract 'error'
|
||||
# fallback to raw content if JSON parsing fails
|
||||
content = json.loads(content).get('error', content)
|
||||
error = json.loads(error).get('error', error)
|
||||
except json.JSONDecodeError:
|
||||
...
|
||||
|
||||
super().__init__(content)
|
||||
self.content = content
|
||||
"Reason for the error."
|
||||
super().__init__(error)
|
||||
self.error = error
|
||||
'Reason for the error.'
|
||||
|
||||
self.status_code = status_code
|
||||
"HTTP status code of the response."
|
||||
'HTTP status code of the response.'
|
||||
|
||||
Generated
+266
-235
@@ -1,14 +1,14 @@
|
||||
# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.2.0"
|
||||
version = "4.3.0"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"},
|
||||
{file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"},
|
||||
{file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
|
||||
{file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -24,13 +24,13 @@ trio = ["trio (>=0.23)"]
|
||||
|
||||
[[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]]
|
||||
@@ -46,63 +46,63 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.3.4"
|
||||
version = "7.4.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "coverage-7.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aff2bd3d585969cc4486bfc69655e862028b689404563e6b549e6a8244f226df"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4353923f38d752ecfbd3f1f20bf7a3546993ae5ecd7c07fd2f25d40b4e54571"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea473c37872f0159294f7073f3fa72f68b03a129799f3533b2bb44d5e9fa4f82"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5214362abf26e254d749fc0c18af4c57b532a4bfde1a057565616dd3b8d7cc94"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99b7d3f7a7adfa3d11e3a48d1a91bb65739555dd6a0d3fa68aa5852d962e5b1"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:74397a1263275bea9d736572d4cf338efaade2de9ff759f9c26bcdceb383bb49"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f154bd866318185ef5865ace5be3ac047b6d1cc0aeecf53bf83fe846f4384d5d"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e0d84099ea7cba9ff467f9c6f747e3fc3906e2aadac1ce7b41add72e8d0a3712"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-win32.whl", hash = "sha256:3f477fb8a56e0c603587b8278d9dbd32e54bcc2922d62405f65574bd76eba78a"},
|
||||
{file = "coverage-7.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:c75738ce13d257efbb6633a049fb2ed8e87e2e6c2e906c52d1093a4d08d67c6b"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:997aa14b3e014339d8101b9886063c5d06238848905d9ad6c6eabe533440a9a7"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a9c5bc5db3eb4cd55ecb8397d8e9b70247904f8eca718cc53c12dcc98e59fc8"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27ee94f088397d1feea3cb524e4313ff0410ead7d968029ecc4bc5a7e1d34fbf"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ce03e25e18dd9bf44723e83bc202114817f3367789052dc9e5b5c79f40cf59d"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85072e99474d894e5df582faec04abe137b28972d5e466999bc64fc37f564a03"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a877810ef918d0d345b783fc569608804f3ed2507bf32f14f652e4eaf5d8f8d0"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9ac17b94ab4ca66cf803f2b22d47e392f0977f9da838bf71d1f0db6c32893cb9"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36d75ef2acab74dc948d0b537ef021306796da551e8ac8b467810911000af66a"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-win32.whl", hash = "sha256:47ee56c2cd445ea35a8cc3ad5c8134cb9bece3a5cb50bb8265514208d0a65928"},
|
||||
{file = "coverage-7.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:11ab62d0ce5d9324915726f611f511a761efcca970bd49d876cf831b4de65be5"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:33e63c578f4acce1b6cd292a66bc30164495010f1091d4b7529d014845cd9bee"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:782693b817218169bfeb9b9ba7f4a9f242764e180ac9589b45112571f32a0ba6"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c4277ddaad9293454da19121c59f2d850f16bcb27f71f89a5c4836906eb35ef"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d892a19ae24b9801771a5a989fb3e850bd1ad2e2b6e83e949c65e8f37bc67a1"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3024ec1b3a221bd10b5d87337d0373c2bcaf7afd86d42081afe39b3e1820323b"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1c3e9d2bbd6f3f79cfecd6f20854f4dc0c6e0ec317df2b265266d0dc06535f1"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e91029d7f151d8bf5ab7d8bfe2c3dbefd239759d642b211a677bc0709c9fdb96"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6879fe41c60080aa4bb59703a526c54e0412b77e649a0d06a61782ecf0853ee1"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-win32.whl", hash = "sha256:fd2f8a641f8f193968afdc8fd1697e602e199931012b574194052d132a79be13"},
|
||||
{file = "coverage-7.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:d1d0ce6c6947a3a4aa5479bebceff2c807b9f3b529b637e2b33dea4468d75fc7"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36797b3625d1da885b369bdaaa3b0d9fb8865caed3c2b8230afaa6005434aa2f"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfed0ec4b419fbc807dec417c401499ea869436910e1ca524cfb4f81cf3f60e7"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f97ff5a9fc2ca47f3383482858dd2cb8ddbf7514427eecf5aa5f7992d0571429"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:607b6c6b35aa49defaebf4526729bd5238bc36fe3ef1a417d9839e1d96ee1e4c"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8e258dcc335055ab59fe79f1dec217d9fb0cdace103d6b5c6df6b75915e7959"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a02ac7c51819702b384fea5ee033a7c202f732a2a2f1fe6c41e3d4019828c8d3"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b710869a15b8caf02e31d16487a931dbe78335462a122c8603bb9bd401ff6fb2"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6a23ae9348a7a92e7f750f9b7e828448e428e99c24616dec93a0720342f241d"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-win32.whl", hash = "sha256:758ebaf74578b73f727acc4e8ab4b16ab6f22a5ffd7dd254e5946aba42a4ce76"},
|
||||
{file = "coverage-7.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:309ed6a559bc942b7cc721f2976326efbfe81fc2b8f601c722bff927328507dc"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aefbb29dc56317a4fcb2f3857d5bce9b881038ed7e5aa5d3bcab25bd23f57328"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:183c16173a70caf92e2dfcfe7c7a576de6fa9edc4119b8e13f91db7ca33a7923"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a4184dcbe4f98d86470273e758f1d24191ca095412e4335ff27b417291f5964"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93698ac0995516ccdca55342599a1463ed2e2d8942316da31686d4d614597ef9"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb220b3596358a86361139edce40d97da7458412d412e1e10c8e1970ee8c09ab"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5b14abde6f8d969e6b9dd8c7a013d9a2b52af1235fe7bebef25ad5c8f47fa18"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:610afaf929dc0e09a5eef6981edb6a57a46b7eceff151947b836d869d6d567c1"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d6ed790728fb71e6b8247bd28e77e99d0c276dff952389b5388169b8ca7b1c28"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-win32.whl", hash = "sha256:c15fdfb141fcf6a900e68bfa35689e1256a670db32b96e7a931cab4a0e1600e5"},
|
||||
{file = "coverage-7.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:38d0b307c4d99a7aca4e00cad4311b7c51b7ac38fb7dea2abe0d182dd4008e05"},
|
||||
{file = "coverage-7.3.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b1e0f25ae99cf247abfb3f0fac7ae25739e4cd96bf1afa3537827c576b4847e5"},
|
||||
{file = "coverage-7.3.4.tar.gz", hash = "sha256:020d56d2da5bc22a0e00a5b0d54597ee91ad72446fa4cf1b97c35022f6b6dbf0"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -138,13 +138,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
description = "A minimal low-level HTTP client."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"},
|
||||
{file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"},
|
||||
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
|
||||
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -155,17 +155,17 @@ h11 = ">=0.13,<0.15"
|
||||
asyncio = ["anyio (>=4.0,<5.0)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
trio = ["trio (>=0.22.0,<0.23.0)"]
|
||||
trio = ["trio (>=0.22.0,<0.25.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.25.2"
|
||||
version = "0.27.0"
|
||||
description = "The next generation HTTP client."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"},
|
||||
{file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"},
|
||||
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
|
||||
{file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -205,160 +205,190 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.3"
|
||||
version = "2.1.5"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
|
||||
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
|
||||
{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]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
version = "24.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "10.1.0"
|
||||
version = "10.4.0"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"},
|
||||
{file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"},
|
||||
{file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"},
|
||||
{file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"},
|
||||
{file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"},
|
||||
{file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"},
|
||||
{file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"},
|
||||
{file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"},
|
||||
{file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"},
|
||||
{file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"},
|
||||
{file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"},
|
||||
{file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"},
|
||||
{file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"},
|
||||
{file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"},
|
||||
{file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
|
||||
{file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
|
||||
docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
|
||||
fpx = ["olefile"]
|
||||
mic = ["olefile"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
typing = ["typing-extensions"]
|
||||
xmp = ["defusedxml"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
version = "1.5.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
|
||||
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
|
||||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
||||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -367,13 +397,13 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.4.3"
|
||||
version = "8.3.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"},
|
||||
{file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"},
|
||||
{file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
|
||||
{file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -381,25 +411,25 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
||||
pluggy = ">=1.5,<2"
|
||||
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.23.2"
|
||||
version = "0.24.0"
|
||||
description = "Pytest support for asyncio"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"},
|
||||
{file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"},
|
||||
{file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"},
|
||||
{file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=7.0.0"
|
||||
pytest = ">=8.2,<9"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
||||
@@ -407,13 +437,13 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "4.1.0"
|
||||
version = "5.0.0"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
|
||||
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
|
||||
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
|
||||
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -421,17 +451,17 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
|
||||
pytest = ">=4.6"
|
||||
|
||||
[package.extras]
|
||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
|
||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-httpserver"
|
||||
version = "1.0.8"
|
||||
version = "1.1.0"
|
||||
description = "pytest-httpserver is a httpserver for pytest"
|
||||
optional = false
|
||||
python-versions = ">=3.8,<4.0"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest_httpserver-1.0.8-py3-none-any.whl", hash = "sha256:24cd3d9f6a0b927c7bfc400d0b3fda7442721b8267ce29942bf307b190f0bb09"},
|
||||
{file = "pytest_httpserver-1.0.8.tar.gz", hash = "sha256:e052f69bc8a9073db02484681e8e47004dd1fb3763b0ae833bd899e5895c559a"},
|
||||
{file = "pytest_httpserver-1.1.0-py3-none-any.whl", hash = "sha256:7ef88be8ed3354b6784daa3daa75a422370327c634053cefb124903fa8d73a41"},
|
||||
{file = "pytest_httpserver-1.1.0.tar.gz", hash = "sha256:6b1cb0199e2ed551b1b94d43f096863bbf6ae5bcd7c75c2c06845e5ce2dc8701"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -439,39 +469,40 @@ Werkzeug = ">=2.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.1.8"
|
||||
version = "0.6.2"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7de792582f6e490ae6aef36a58d85df9f7a0cfd1b0d4fe6b4fb51803a3ac96fa"},
|
||||
{file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8e3255afd186c142eef4ec400d7826134f028a85da2146102a1172ecc7c3696"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff78a7583020da124dd0deb835ece1d87bb91762d40c514ee9b67a087940528b"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd8ee69b02e7bdefe1e5da2d5b6eaaddcf4f90859f00281b2333c0e3a0cc9cd6"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a05b0ddd7ea25495e4115a43125e8a7ebed0aa043c3d432de7e7d6e8e8cd6448"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e6f08ca730f4dc1b76b473bdf30b1b37d42da379202a059eae54ec7fc1fbcfed"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f35960b02df6b827c1b903091bb14f4b003f6cf102705efc4ce78132a0aa5af3"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d076717c67b34c162da7c1a5bda16ffc205e0e0072c03745275e7eab888719f"},
|
||||
{file = "ruff-0.1.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a21ab023124eafb7cef6d038f835cb1155cd5ea798edd8d9eb2f8b84be07d9"},
|
||||
{file = "ruff-0.1.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ce697c463458555027dfb194cb96d26608abab920fa85213deb5edf26e026664"},
|
||||
{file = "ruff-0.1.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db6cedd9ffed55548ab313ad718bc34582d394e27a7875b4b952c2d29c001b26"},
|
||||
{file = "ruff-0.1.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:05ffe9dbd278965271252704eddb97b4384bf58b971054d517decfbf8c523f05"},
|
||||
{file = "ruff-0.1.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5daaeaf00ae3c1efec9742ff294b06c3a2a9db8d3db51ee4851c12ad385cda30"},
|
||||
{file = "ruff-0.1.8-py3-none-win32.whl", hash = "sha256:e49fbdfe257fa41e5c9e13c79b9e79a23a79bd0e40b9314bc53840f520c2c0b3"},
|
||||
{file = "ruff-0.1.8-py3-none-win_amd64.whl", hash = "sha256:f41f692f1691ad87f51708b823af4bb2c5c87c9248ddd3191c8f088e66ce590a"},
|
||||
{file = "ruff-0.1.8-py3-none-win_arm64.whl", hash = "sha256:aa8ee4f8440023b0a6c3707f76cadce8657553655dcbb5fc9b2f9bb9bee389f6"},
|
||||
{file = "ruff-0.1.8.tar.gz", hash = "sha256:f7ee467677467526cfe135eab86a40a0e8db43117936ac4f9b469ce9cdb3fb62"},
|
||||
{file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"},
|
||||
{file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"},
|
||||
{file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"},
|
||||
{file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"},
|
||||
{file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"},
|
||||
{file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"},
|
||||
{file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
|
||||
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -487,13 +518,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.9.0"
|
||||
version = "4.10.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
|
||||
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
|
||||
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
|
||||
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -516,4 +547,4 @@ watchdog = ["watchdog (>=2.3)"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "9416a897c95d3c80cf1bfd3cc61cd19f0143c9bd0bc7c219fcb31ee27c497c9d"
|
||||
content-hash = "e36516c932ab9dd7497acc0c3d55ab2c963004595efe97c2bc80854687c32c1e"
|
||||
|
||||
+9
-6
@@ -10,15 +10,15 @@ repository = "https://github.com/jmorganca/ollama-python"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
httpx = "^0.25.2"
|
||||
httpx = "^0.27.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.3"
|
||||
pytest-asyncio = "^0.23.2"
|
||||
pytest-cov = "^4.1.0"
|
||||
pytest = ">=7.4.3,<9.0.0"
|
||||
pytest-asyncio = ">=0.23.2,<0.25.0"
|
||||
pytest-cov = ">=4.1,<6.0"
|
||||
pytest-httpserver = "^1.0.8"
|
||||
pillow = "^10.1.0"
|
||||
ruff = "^0.1.8"
|
||||
pillow = "^10.2.0"
|
||||
ruff = ">=0.1.8,<0.7.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
@@ -35,3 +35,6 @@ indent-style = "space"
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "B"]
|
||||
ignore = ["E501"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = '--doctest-modules --ignore examples'
|
||||
|
||||
+18
-18
@@ -1,27 +1,27 @@
|
||||
anyio==4.2.0 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee \
|
||||
--hash=sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f
|
||||
certifi==2023.11.17 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1 \
|
||||
--hash=sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474
|
||||
anyio==4.3.0 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8 \
|
||||
--hash=sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6
|
||||
certifi==2024.2.2 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
|
||||
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
|
||||
exceptiongroup==1.2.0 ; python_version >= "3.8" and python_version < "3.11" \
|
||||
--hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \
|
||||
--hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68
|
||||
h11==0.14.0 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
|
||||
--hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
|
||||
httpcore==1.0.2 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7 \
|
||||
--hash=sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535
|
||||
httpx==0.25.2 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8 \
|
||||
--hash=sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118
|
||||
httpcore==1.0.4 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73 \
|
||||
--hash=sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022
|
||||
httpx==0.27.0 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \
|
||||
--hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5
|
||||
idna==3.6 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
sniffio==1.3.0 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
--hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
|
||||
typing-extensions==4.9.0 ; python_version >= "3.8" and python_version < "3.11" \
|
||||
--hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \
|
||||
--hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd
|
||||
sniffio==1.3.1 ; python_version >= "3.8" and python_version < "4.0" \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
typing-extensions==4.10.0 ; python_version >= "3.8" and python_version < "3.11" \
|
||||
--hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \
|
||||
--hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb
|
||||
|
||||
+232
-17
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import io
|
||||
import json
|
||||
import types
|
||||
import pytest
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
@@ -27,9 +26,11 @@ def test_client_chat(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'messages': [{'role': 'user', 'content': 'Why is the sky blue?'}],
|
||||
'tools': [],
|
||||
'stream': False,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json(
|
||||
{
|
||||
@@ -73,17 +74,21 @@ def test_client_chat_stream(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'messages': [{'role': 'user', 'content': 'Why is the sky blue?'}],
|
||||
'tools': [],
|
||||
'stream': True,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = Client(httpserver.url_for('/'))
|
||||
response = client.chat('dummy', messages=[{'role': 'user', 'content': 'Why is the sky blue?'}], stream=True)
|
||||
|
||||
it = iter(['I ', "don't ", 'know.'])
|
||||
for part in response:
|
||||
assert part['message']['role'] in 'assistant'
|
||||
assert part['message']['content'] in ['I ', "don't ", 'know.']
|
||||
assert part['message']['content'] == next(it)
|
||||
|
||||
|
||||
def test_client_chat_images(httpserver: HTTPServer):
|
||||
@@ -99,9 +104,11 @@ def test_client_chat_images(httpserver: HTTPServer):
|
||||
'images': ['iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'],
|
||||
},
|
||||
],
|
||||
'tools': [],
|
||||
'stream': False,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json(
|
||||
{
|
||||
@@ -130,6 +137,7 @@ def test_client_generate(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -138,6 +146,7 @@ def test_client_generate(httpserver: HTTPServer):
|
||||
'images': [],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json(
|
||||
{
|
||||
@@ -174,6 +183,7 @@ def test_client_generate_stream(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -182,14 +192,17 @@ def test_client_generate_stream(httpserver: HTTPServer):
|
||||
'images': [],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = Client(httpserver.url_for('/'))
|
||||
response = client.generate('dummy', 'Why is the sky blue?', stream=True)
|
||||
|
||||
it = iter(['Because ', 'it ', 'is.'])
|
||||
for part in response:
|
||||
assert part['model'] == 'dummy'
|
||||
assert part['response'] in ['Because ', 'it ', 'is.']
|
||||
assert part['response'] == next(it)
|
||||
|
||||
|
||||
def test_client_generate_images(httpserver: HTTPServer):
|
||||
@@ -199,6 +212,7 @@ def test_client_generate_images(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -207,6 +221,7 @@ def test_client_generate_images(httpserver: HTTPServer):
|
||||
'images': ['iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json(
|
||||
{
|
||||
@@ -263,11 +278,14 @@ def test_client_pull_stream(httpserver: HTTPServer):
|
||||
'insecure': False,
|
||||
'stream': True,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = Client(httpserver.url_for('/'))
|
||||
response = client.pull('dummy', stream=True)
|
||||
assert isinstance(response, types.GeneratorType)
|
||||
|
||||
it = iter(['pulling manifest', 'verifying sha256 digest', 'writing manifest', 'removing any unused layers', 'success'])
|
||||
for part in response:
|
||||
assert part['status'] == next(it)
|
||||
|
||||
|
||||
def test_client_push(httpserver: HTTPServer):
|
||||
@@ -287,6 +305,14 @@ def test_client_push(httpserver: HTTPServer):
|
||||
|
||||
|
||||
def test_client_push_stream(httpserver: HTTPServer):
|
||||
def stream_handler(_: Request):
|
||||
def generate():
|
||||
yield json.dumps({'status': 'retrieving manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'pushing manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'success'}) + '\n'
|
||||
|
||||
return Response(generate())
|
||||
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/push',
|
||||
method='POST',
|
||||
@@ -295,11 +321,14 @@ def test_client_push_stream(httpserver: HTTPServer):
|
||||
'insecure': False,
|
||||
'stream': True,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = Client(httpserver.url_for('/'))
|
||||
response = client.push('dummy', stream=True)
|
||||
assert isinstance(response, types.GeneratorType)
|
||||
|
||||
it = iter(['retrieving manifest', 'pushing manifest', 'success'])
|
||||
for part in response:
|
||||
assert part['status'] == next(it)
|
||||
|
||||
|
||||
def test_client_create_path(httpserver: HTTPServer):
|
||||
@@ -311,6 +340,7 @@ def test_client_create_path(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -334,6 +364,7 @@ def test_client_create_path_relative(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -366,6 +397,7 @@ def test_client_create_path_user_home(httpserver: HTTPServer, userhomedir):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -389,6 +421,7 @@ def test_client_create_modelfile(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -399,14 +432,64 @@ def test_client_create_modelfile(httpserver: HTTPServer):
|
||||
assert isinstance(response, dict)
|
||||
|
||||
|
||||
def test_client_create_modelfile_roundtrip(httpserver: HTTPServer):
|
||||
httpserver.expect_ordered_request(PrefixPattern('/api/blobs/'), method='HEAD').respond_with_response(Response(status=200))
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/create',
|
||||
method='POST',
|
||||
json={
|
||||
'name': 'dummy',
|
||||
'modelfile': '''FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
TEMPLATE """[INST] <<SYS>>{{.System}}<</SYS>>
|
||||
{{.Prompt}} [/INST]"""
|
||||
SYSTEM """
|
||||
Use
|
||||
multiline
|
||||
strings.
|
||||
"""
|
||||
PARAMETER stop [INST]
|
||||
PARAMETER stop [/INST]
|
||||
PARAMETER stop <<SYS>>
|
||||
PARAMETER stop <</SYS>>''',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
client = Client(httpserver.url_for('/'))
|
||||
|
||||
with tempfile.NamedTemporaryFile() as blob:
|
||||
response = client.create(
|
||||
'dummy',
|
||||
modelfile='\n'.join(
|
||||
[
|
||||
f'FROM {blob.name}',
|
||||
'TEMPLATE """[INST] <<SYS>>{{.System}}<</SYS>>',
|
||||
'{{.Prompt}} [/INST]"""',
|
||||
'SYSTEM """',
|
||||
'Use',
|
||||
'multiline',
|
||||
'strings.',
|
||||
'"""',
|
||||
'PARAMETER stop [INST]',
|
||||
'PARAMETER stop [/INST]',
|
||||
'PARAMETER stop <<SYS>>',
|
||||
'PARAMETER stop <</SYS>>',
|
||||
]
|
||||
),
|
||||
)
|
||||
assert isinstance(response, dict)
|
||||
|
||||
|
||||
def test_client_create_from_library(httpserver: HTTPServer):
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/create',
|
||||
method='POST',
|
||||
json={
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM llama2\n',
|
||||
'modelfile': 'FROM llama2',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -445,9 +528,11 @@ async def test_async_client_chat(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'messages': [{'role': 'user', 'content': 'Why is the sky blue?'}],
|
||||
'tools': [],
|
||||
'stream': False,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -458,21 +543,45 @@ async def test_async_client_chat(httpserver: HTTPServer):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_chat_stream(httpserver: HTTPServer):
|
||||
def stream_handler(_: Request):
|
||||
def generate():
|
||||
for message in ['I ', "don't ", 'know.']:
|
||||
yield (
|
||||
json.dumps(
|
||||
{
|
||||
'model': 'dummy',
|
||||
'message': {
|
||||
'role': 'assistant',
|
||||
'content': message,
|
||||
},
|
||||
}
|
||||
)
|
||||
+ '\n'
|
||||
)
|
||||
|
||||
return Response(generate())
|
||||
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/chat',
|
||||
method='POST',
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'messages': [{'role': 'user', 'content': 'Why is the sky blue?'}],
|
||||
'tools': [],
|
||||
'stream': True,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = AsyncClient(httpserver.url_for('/'))
|
||||
response = await client.chat('dummy', messages=[{'role': 'user', 'content': 'Why is the sky blue?'}], stream=True)
|
||||
assert isinstance(response, types.AsyncGeneratorType)
|
||||
|
||||
it = iter(['I ', "don't ", 'know.'])
|
||||
async for part in response:
|
||||
assert part['message']['role'] == 'assistant'
|
||||
assert part['message']['content'] == next(it)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -489,9 +598,11 @@ async def test_async_client_chat_images(httpserver: HTTPServer):
|
||||
'images': ['iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'],
|
||||
},
|
||||
],
|
||||
'tools': [],
|
||||
'stream': False,
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -511,6 +622,7 @@ async def test_async_client_generate(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -519,6 +631,7 @@ async def test_async_client_generate(httpserver: HTTPServer):
|
||||
'images': [],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -529,12 +642,28 @@ async def test_async_client_generate(httpserver: HTTPServer):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_generate_stream(httpserver: HTTPServer):
|
||||
def stream_handler(_: Request):
|
||||
def generate():
|
||||
for message in ['Because ', 'it ', 'is.']:
|
||||
yield (
|
||||
json.dumps(
|
||||
{
|
||||
'model': 'dummy',
|
||||
'response': message,
|
||||
}
|
||||
)
|
||||
+ '\n'
|
||||
)
|
||||
|
||||
return Response(generate())
|
||||
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/generate',
|
||||
method='POST',
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -543,12 +672,17 @@ async def test_async_client_generate_stream(httpserver: HTTPServer):
|
||||
'images': [],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = AsyncClient(httpserver.url_for('/'))
|
||||
response = await client.generate('dummy', 'Why is the sky blue?', stream=True)
|
||||
assert isinstance(response, types.AsyncGeneratorType)
|
||||
|
||||
it = iter(['Because ', 'it ', 'is.'])
|
||||
async for part in response:
|
||||
assert part['model'] == 'dummy'
|
||||
assert part['response'] == next(it)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -559,6 +693,7 @@ async def test_async_client_generate_images(httpserver: HTTPServer):
|
||||
json={
|
||||
'model': 'dummy',
|
||||
'prompt': 'Why is the sky blue?',
|
||||
'suffix': '',
|
||||
'system': '',
|
||||
'template': '',
|
||||
'context': [],
|
||||
@@ -567,6 +702,7 @@ async def test_async_client_generate_images(httpserver: HTTPServer):
|
||||
'images': ['iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'],
|
||||
'format': '',
|
||||
'options': {},
|
||||
'keep_alive': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -597,6 +733,16 @@ async def test_async_client_pull(httpserver: HTTPServer):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_pull_stream(httpserver: HTTPServer):
|
||||
def stream_handler(_: Request):
|
||||
def generate():
|
||||
yield json.dumps({'status': 'pulling manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'verifying sha256 digest'}) + '\n'
|
||||
yield json.dumps({'status': 'writing manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'removing any unused layers'}) + '\n'
|
||||
yield json.dumps({'status': 'success'}) + '\n'
|
||||
|
||||
return Response(generate())
|
||||
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/pull',
|
||||
method='POST',
|
||||
@@ -605,11 +751,14 @@ async def test_async_client_pull_stream(httpserver: HTTPServer):
|
||||
'insecure': False,
|
||||
'stream': True,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = AsyncClient(httpserver.url_for('/'))
|
||||
response = await client.pull('dummy', stream=True)
|
||||
assert isinstance(response, types.AsyncGeneratorType)
|
||||
|
||||
it = iter(['pulling manifest', 'verifying sha256 digest', 'writing manifest', 'removing any unused layers', 'success'])
|
||||
async for part in response:
|
||||
assert part['status'] == next(it)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -631,6 +780,14 @@ async def test_async_client_push(httpserver: HTTPServer):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_push_stream(httpserver: HTTPServer):
|
||||
def stream_handler(_: Request):
|
||||
def generate():
|
||||
yield json.dumps({'status': 'retrieving manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'pushing manifest'}) + '\n'
|
||||
yield json.dumps({'status': 'success'}) + '\n'
|
||||
|
||||
return Response(generate())
|
||||
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/push',
|
||||
method='POST',
|
||||
@@ -639,11 +796,14 @@ async def test_async_client_push_stream(httpserver: HTTPServer):
|
||||
'insecure': False,
|
||||
'stream': True,
|
||||
},
|
||||
).respond_with_json({})
|
||||
).respond_with_handler(stream_handler)
|
||||
|
||||
client = AsyncClient(httpserver.url_for('/'))
|
||||
response = await client.push('dummy', stream=True)
|
||||
assert isinstance(response, types.AsyncGeneratorType)
|
||||
|
||||
it = iter(['retrieving manifest', 'pushing manifest', 'success'])
|
||||
async for part in response:
|
||||
assert part['status'] == next(it)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -656,6 +816,7 @@ async def test_async_client_create_path(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -680,6 +841,7 @@ async def test_async_client_create_path_relative(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -704,6 +866,7 @@ async def test_async_client_create_path_user_home(httpserver: HTTPServer, userho
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -728,6 +891,7 @@ async def test_async_client_create_modelfile(httpserver: HTTPServer):
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
@@ -738,6 +902,56 @@ async def test_async_client_create_modelfile(httpserver: HTTPServer):
|
||||
assert isinstance(response, dict)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_create_modelfile_roundtrip(httpserver: HTTPServer):
|
||||
httpserver.expect_ordered_request(PrefixPattern('/api/blobs/'), method='HEAD').respond_with_response(Response(status=200))
|
||||
httpserver.expect_ordered_request(
|
||||
'/api/create',
|
||||
method='POST',
|
||||
json={
|
||||
'name': 'dummy',
|
||||
'modelfile': '''FROM @sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
TEMPLATE """[INST] <<SYS>>{{.System}}<</SYS>>
|
||||
{{.Prompt}} [/INST]"""
|
||||
SYSTEM """
|
||||
Use
|
||||
multiline
|
||||
strings.
|
||||
"""
|
||||
PARAMETER stop [INST]
|
||||
PARAMETER stop [/INST]
|
||||
PARAMETER stop <<SYS>>
|
||||
PARAMETER stop <</SYS>>''',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
client = AsyncClient(httpserver.url_for('/'))
|
||||
|
||||
with tempfile.NamedTemporaryFile() as blob:
|
||||
response = await client.create(
|
||||
'dummy',
|
||||
modelfile='\n'.join(
|
||||
[
|
||||
f'FROM {blob.name}',
|
||||
'TEMPLATE """[INST] <<SYS>>{{.System}}<</SYS>>',
|
||||
'{{.Prompt}} [/INST]"""',
|
||||
'SYSTEM """',
|
||||
'Use',
|
||||
'multiline',
|
||||
'strings.',
|
||||
'"""',
|
||||
'PARAMETER stop [INST]',
|
||||
'PARAMETER stop [/INST]',
|
||||
'PARAMETER stop <<SYS>>',
|
||||
'PARAMETER stop <</SYS>>',
|
||||
]
|
||||
),
|
||||
)
|
||||
assert isinstance(response, dict)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_client_create_from_library(httpserver: HTTPServer):
|
||||
httpserver.expect_ordered_request(
|
||||
@@ -745,8 +959,9 @@ async def test_async_client_create_from_library(httpserver: HTTPServer):
|
||||
method='POST',
|
||||
json={
|
||||
'name': 'dummy',
|
||||
'modelfile': 'FROM llama2\n',
|
||||
'modelfile': 'FROM llama2',
|
||||
'stream': False,
|
||||
'quantize': None,
|
||||
},
|
||||
).respond_with_json({})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user