[0.0.5] - 03-14-2024 Dialectic Agent (#38)

* 🧪 asyncify tests

*  asyncify client

* Basic Test for Page based pagination

* add sync buildstep and client

* add vscode DX

* Added Testing for generators and updated examples

* feat: example updates

* readme exists now

* Stylistic changes and generic message

* Metamessages with other refactoring - untested

* Work with unit tests

* Fix Examples

* MEME-78 Update Changelogs

* Docstrings to client

* 🧪 autogenerate sync tests

* test one

* add db type

* sync client

* add status badge

* add coverage

* add file

* give perms

* properly output coverage

* split test and coverage

* rename action

* 🧪 autogenerate sync tests (#16)

* Vector Support (#18)

* Scaffold for PGVector support

* Buggy crud with logic skeleton on api

* Crud logic and schema definition for pgvector

* Populate all routes and refactor to name Collection

* vince's progress

* AsyncCollection progress

* Local PGVector Docker Container

* client methods for sdk except document delete and update

* Vector Support Passing All Test Cases

* Docs Updates

---------

Co-authored-by: vintro <vince@plasticlabs.ai>

* Add reverse parameters for paginated routes

* Address dependabot

* Formatting

* initial commit on honcho dspy personas

* working, hit token limit and can't test dspy optimization

* initial version working, need to test optimization

* optimizers working, but appending any example

* ready for user object (tbomk)

* Revert "add test actions and coverage"

* Refactor to add User and App Tables

* User Object passing test cases

* Update examples

* DSPy Todo and documentation updates

* Add is_active filtering

* Add is_active filtering to the generator

* Fix update user metadata

* working, but weird compiler error

* fixed str error in optimizer

* ship

* sentry

* Open Telemetry

* optional logging with environment variables

* add actions again? (#29)

* add postgres

* add openai key

* readd coverage

* desyncify and add detailed coverage

* ⚙️ chore: update start script in VS Code to include poetry install --no-root before running uvicorn (#33)

* Refactored code but need to tweak asyncpg

* Working Async API using Psycopg3

* Update Workflow Connection URI

* Update Workflow Connection URI in coverage test as well

* Skeleton for Dialectic API

* Fixes DEV-217 URL Encoding

* Add Built-in Langchain Utility function

* Sphinx Docs MVP

* Metadata filtering for all fixes dev-261

* Basic Dialectic Endpoint fixes dev-253

* Working Fact Deriver

* 0.0.5 Docs and README updates

* Cloudflare Sphinx

---------

Co-authored-by: hyusap <paulayush@gmail.com>
Co-authored-by: vintro <vince@plasticlabs.ai>
This commit is contained in:
Vineeth Voruganti 2024-03-14 10:01:08 -07:00 committed by GitHub
parent 611981f372
commit 995a6d0644
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 5479 additions and 1123 deletions

47
.github/workflows/run_coverage.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Run Coverage
on: [pull_request]
jobs:
test:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install poetry
run: |
pip install poetry
- name: Start Database
run: |
cd api/local
docker compose up --wait
cd ../..
- name: Start Server
run: |
cd api
poetry install --no-root
poetry run uvicorn src.main:app &
sleep 5
cd ..
env:
DATABASE_TYPE: postgres
CONNECTION_URI: postgresql+psycopg://testuser:testpwd@localhost:5432/honcho
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Run Tests
run: |
cd sdk
poetry install
poetry run coverage run -m pytest
poetry run coverage report --format=markdown > coverage.md
cd ..
- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
with:
recreate: true
path: sdk/coverage.md
- name: Stop Server
run: |
kill $(jobs -p) || true

46
.github/workflows/run_tests.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Run Tests
on: [push, pull_request]
jobs:
test:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install poetry
run: |
pip install poetry
- name: Start Database
run: |
cd api/local
docker compose up --wait
cd ../..
- name: Start Server
run: |
cd api
poetry install --no-root
poetry run uvicorn src.main:app &
sleep 5
cd ..
env:
DATABASE_TYPE: postgres
CONNECTION_URI: postgresql+psycopg://testuser:testpwd@localhost:5432/honcho
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Run Tests
run: |
cd sdk
poetry install
poetry run pytest
cd ..
- name: Stop Database
run: |
cd api/local
docker compose down
cd ../..
- name: Stop Server
run: |
kill $(jobs -p) || true

4
.gitignore vendored
View File

@ -74,7 +74,7 @@ instance/
.scrapy
# Sphinx documentation
docs/_build/
**/docs/_build/
# PyBuilder
.pybuilder/
@ -165,3 +165,5 @@ cython_debug/
#.idea/
.DS_Store
supabase/

View File

@ -1,5 +1,5 @@
# Honcho
![Static Badge](https://img.shields.io/badge/Version-0.0.4-blue)
![Static Badge](https://img.shields.io/badge/Version-0.0.5-blue)
[![Discord](https://img.shields.io/discord/1016845111637839922?style=flat&logo=discord&logoColor=23ffffff&label=Plastic%20Labs&labelColor=235865F2)](https://discord.gg/plasticlabs)
![GitHub License](https://img.shields.io/github/license/plastic-labs/honcho)
![GitHub Repo stars](https://img.shields.io/github/stars/plastic-labs/honcho)

View File

@ -1,7 +1,28 @@
DATABASE_TYPE=sqlite
CONNECTION_URI=sqlite://
DATABASE_TYPE=postgres
CONNECTION_URI=postgresql://testuser:testpwd@localhost:5432/honcho
CONNECTION_URI=postgresql+psycopg://testuser:testpwd@localhost:5432/honcho
OPENAI_API_KEY=
# Logging
OPENTELEMETRY_ENABLED=false # Set to true to enable OpenTelemetry logging and tracing
SENTRY_ENABLED=false # Set to true to enable Sentry logging and tracing
## Sentry
SENTRY_DSN=
## Open Telemetry
OTEL_SERVICE_NAME=honcho
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
OTEL_PYTHON_LOG_CORRELATION=true
OTEL_PYTHON_LOG_LEVEL=
OTEL_EXPORTER_OTLP_PROTOCOL=
OTEL_EXPORTER_OTLP_ENDPOINT=
OTEL_EXPORTER_OTLP_HEADERS=
OTEL_RESOURCE_ATTRIBUTES=
DEBUG_LOG_OTEL_TO_PROVIDER=false
DEBUG_LOG_OTEL_TO_CONSOLE=true

View File

@ -4,7 +4,7 @@
{
"label": "start",
"type": "shell",
"command": "poetry run uvicorn src.main:app --reload",
"command": "poetry install --no-root && poetry run uvicorn src.main:app --reload",
"group": "none",
"presentation": {
"reveal": "always",
@ -12,7 +12,8 @@
},
"runOptions": {
"runOn": "folderOpen"
}
},
"problemMatcher": []
}
]
}

View File

@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.0.5] — 2024-03-14
### Added
* Metadata to all data primitives (Users, Sessions, Messages, etc.)
* Ability to filter paginated GET requests by JSON filter based on metadata
* Optional Sentry error monitoring
* Optional Opentelemetry logging
* Dialectic API to interact with honcho agent and get insights about users
* Automatic Fact Derivation Script for automatically generating simple memory
### Changed
* API Server now uses async methods to make use of benefits of FastAPI
## [0.0.4] — 2024-02-22
### Added

View File

@ -14,10 +14,11 @@ kill_timeout = "5s"
[processes]
api = "python -m uvicorn src.main:app --host 0.0.0.0 --port 8000"
deriver = "python -m src.harvester"
[http_service]
internal_port = 8000
auto_stop_machines = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1
processes = ["api"]

2216
api/poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,32 @@
[tool.poetry]
name = "honcho"
version = "0.0.4"
version = "0.0.5"
description = "Honcho Server"
authors = ["Plastic Labs <hello@plasticlabs.ai>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
python = "^3.8.1"
fastapi = "^0.109.0"
uvicorn = "^0.24.0.post1"
python-dotenv = "^1.0.0"
sqlalchemy = "^2.0.25"
psycopg2-binary = "^2.9.9"
slowapi = "^0.1.8"
fastapi-pagination = "^0.12.14"
pgvector = "^0.2.5"
openai = "^1.12.0"
sentry-sdk = {extras = ["fastapi", "sqlalchemy"], version = "^1.40.5"}
opentelemetry-instrumentation-fastapi = "^0.44b0"
opentelemetry-api = "^1.23.0"
opentelemetry-sdk = "^1.23.0"
opentelemetry-exporter-otlp = "^1.23.0"
opentelemetry-instrumentation-sqlalchemy = "^0.44b0"
opentelemetry-instrumentation-logging = "^0.44b0"
greenlet = "^3.0.3"
realtime = "^1.0.2"
psycopg = {extras = ["binary"], version = "^3.1.18"}
langchain = "^0.1.12"
langchain-openai = "^0.0.8"
[tool.ruff.lint]
# from https://docs.astral.sh/ruff/linter/#rule-selection example
@ -33,6 +44,8 @@ select = [
# isort
"I",
]
[tool.ruff.flake8-bugbear]
extend-immutable-calls = ["fastapi.Depends"]
[build-system]

76
api/src/agent.py Normal file
View File

@ -0,0 +1,76 @@
import os
import uuid
from typing import Optional
from dotenv import load_dotenv
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
load_prompt,
)
from langchain_openai import ChatOpenAI
from sqlalchemy.ext.asyncio import AsyncSession
from . import crud, schemas
load_dotenv()
# from supabase import Client
SYSTEM_DIALECTIC = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/dialectic.yaml")
)
system_dialectic: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_DIALECTIC
)
llm: ChatOpenAI = ChatOpenAI(model_name="gpt-4")
async def chat(
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
query: str,
db: AsyncSession,
):
collection = await crud.get_collection_by_name(db, app_id, user_id, "honcho")
retrieved_facts = None
if collection is None:
collection_create = schemas.CollectionCreate(name="honcho", metadata={})
collection = await crud.create_collection(
db,
collection=collection_create,
app_id=app_id,
user_id=user_id,
)
else:
retrieved_documents = await crud.query_documents(
db=db,
app_id=app_id,
user_id=user_id,
collection_id=collection.id,
query=query,
top_k=1,
)
if len(retrieved_documents) > 0:
retrieved_facts = retrieved_documents[0].content
dialectic_prompt = ChatPromptTemplate.from_messages([system_dialectic])
chain = dialectic_prompt | llm
response = await chain.ainvoke(
{
"agent_input": query,
"retrieved_facts": retrieved_facts if retrieved_facts else "None",
}
)
return schemas.AgentChat(content=response.content)
async def hydrate():
pass
async def insight():
pass

View File

@ -5,8 +5,9 @@ from typing import Optional, Sequence
from openai import OpenAI
from sqlalchemy import Select, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
# from sqlalchemy.orm import Session
from . import models, schemas
openai_client = OpenAI()
@ -16,32 +17,36 @@ openai_client = OpenAI()
########################################################
def get_app(db: Session, app_id: uuid.UUID) -> Optional[models.App]:
async def get_app(db: AsyncSession, app_id: uuid.UUID) -> Optional[models.App]:
stmt = select(models.App).where(models.App.id == app_id)
app = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
app = result.scalar_one_or_none()
return app
def get_app_by_name(db: Session, name: str) -> Optional[models.App]:
async def get_app_by_name(db: AsyncSession, name: str) -> Optional[models.App]:
stmt = select(models.App).where(models.App.name == name)
app = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
app = result.scalar_one_or_none()
return app
# def get_apps(db: Session) -> Sequence[models.App]:
# def get_apps(db: AsyncSession) -> Sequence[models.App]:
# return db.query(models.App).all()
def create_app(db: Session, app: schemas.AppCreate) -> models.App:
async def create_app(db: AsyncSession, app: schemas.AppCreate) -> models.App:
honcho_app = models.App(name=app.name, h_metadata=app.metadata)
db.add(honcho_app)
db.commit()
db.refresh(honcho_app)
await db.commit()
await db.refresh(honcho_app)
return honcho_app
def update_app(db: Session, app_id: uuid.UUID, app: schemas.AppUpdate) -> models.App:
honcho_app = get_app(db, app_id)
async def update_app(
db: AsyncSession, app_id: uuid.UUID, app: schemas.AppUpdate
) -> models.App:
honcho_app = await get_app(db, app_id)
if honcho_app is None:
raise ValueError("App not found")
if app.name is not None:
@ -49,12 +54,12 @@ def update_app(db: Session, app_id: uuid.UUID, app: schemas.AppUpdate) -> models
if app.metadata is not None:
honcho_app.h_metadata = app.metadata
db.commit()
db.refresh(honcho_app)
await db.commit()
await db.refresh(honcho_app)
return honcho_app
# def delete_app(db: Session, app_id: uuid.UUID) -> bool:
# def delete_app(db: AsyncSession, app_id: uuid.UUID) -> bool:
# existing_app = get_app(db, app_id)
# if existing_app is None:
# return False
@ -68,8 +73,8 @@ def update_app(db: Session, app_id: uuid.UUID, app: schemas.AppUpdate) -> models
########################################################
def create_user(
db: Session, app_id: uuid.UUID, user: schemas.UserCreate
async def create_user(
db: AsyncSession, app_id: uuid.UUID, user: schemas.UserCreate
) -> models.User:
honcho_user = models.User(
app_id=app_id,
@ -77,37 +82,48 @@ def create_user(
h_metadata=user.metadata,
)
db.add(honcho_user)
db.commit()
db.refresh(honcho_user)
await db.commit()
await db.refresh(honcho_user)
return honcho_user
def get_user(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID
async def get_user(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID
) -> Optional[models.User]:
stmt = (
select(models.User)
.where(models.User.app_id == app_id)
.where(models.User.id == user_id)
)
user = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
user = result.scalar_one_or_none()
return user
def get_user_by_name(
db: Session, app_id: uuid.UUID, name: str
async def get_user_by_name(
db: AsyncSession, app_id: uuid.UUID, name: str
) -> Optional[models.User]:
stmt = (
select(models.User)
.where(models.User.app_id == app_id)
.where(models.User.name == name)
)
user = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
user = result.scalar_one_or_none()
return user
def get_users(db: Session, app_id: uuid.UUID, reverse: bool = False) -> Select:
async def get_users(
db: AsyncSession,
app_id: uuid.UUID,
reverse: bool = False,
filter: Optional[dict] = None,
) -> Select:
stmt = select(models.User).where(models.User.app_id == app_id)
if filter is not None:
stmt = stmt.where(models.User.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.User.created_at.desc())
else:
@ -116,10 +132,10 @@ def get_users(db: Session, app_id: uuid.UUID, reverse: bool = False) -> Select:
return stmt
def update_user(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, user: schemas.UserUpdate
async def update_user(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID, user: schemas.UserUpdate
) -> models.User:
honcho_user = get_user(db, app_id, user_id)
honcho_user = await get_user(db, app_id, user_id)
if honcho_user is None:
raise ValueError("User not found")
if user.name is not None:
@ -127,12 +143,12 @@ def update_user(
if user.metadata is not None:
honcho_user.h_metadata = user.metadata
db.commit()
db.refresh(honcho_user)
await db.commit()
await db.refresh(honcho_user)
return honcho_user
# def delete_user(db: Session, app_id: uuid.UUID, user_id: uuid.UUID) -> bool:
# def delete_user(db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID) -> bool:
# existing_user = get_user(db, app_id, user_id)
# if existing_user is None:
# return False
@ -145,8 +161,8 @@ def update_user(
########################################################
def get_session(
db: Session,
async def get_session(
db: AsyncSession,
app_id: uuid.UUID,
session_id: uuid.UUID,
user_id: Optional[uuid.UUID] = None,
@ -159,17 +175,19 @@ def get_session(
)
if user_id is not None:
stmt = stmt.where(models.Session.user_id == user_id)
session = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
session = result.scalar_one_or_none()
return session
def get_sessions(
db: Session,
async def get_sessions(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
location_id: Optional[str] = None,
reverse: Optional[bool] = False,
is_active: Optional[bool] = False,
filter: Optional[dict] = None,
) -> Select:
stmt = (
select(models.Session)
@ -181,6 +199,9 @@ def get_sessions(
if is_active:
stmt = stmt.where(models.Session.is_active.is_(True))
if filter is not None:
stmt = stmt.where(models.Session.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.Session.created_at.desc())
else:
@ -192,28 +213,34 @@ def get_sessions(
return stmt
def create_session(
db: Session, session: schemas.SessionCreate, app_id: uuid.UUID, user_id: uuid.UUID
async def create_session(
db: AsyncSession,
session: schemas.SessionCreate,
app_id: uuid.UUID,
user_id: uuid.UUID,
) -> models.Session:
honcho_user = await get_user(db, app_id=app_id, user_id=user_id)
if honcho_user is None:
raise ValueError("User not found")
honcho_session = models.Session(
user_id=user_id,
location_id=session.location_id,
h_metadata=session.metadata,
)
db.add(honcho_session)
db.commit()
db.refresh(honcho_session)
await db.commit()
await db.refresh(honcho_session)
return honcho_session
def update_session(
db: Session,
async def update_session(
db: AsyncSession,
session: schemas.SessionUpdate,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
) -> bool:
honcho_session = get_session(
honcho_session = await get_session(
db, app_id=app_id, session_id=session_id, user_id=user_id
)
if honcho_session is None:
@ -222,13 +249,13 @@ def update_session(
session.metadata is not None
): # Need to explicitly be there won't make it empty by default
honcho_session.h_metadata = session.metadata
db.commit()
db.refresh(honcho_session)
await db.commit()
await db.refresh(honcho_session)
return honcho_session
def delete_session(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, session_id: uuid.UUID
async def delete_session(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID, session_id: uuid.UUID
) -> bool:
stmt = (
select(models.Session)
@ -237,11 +264,12 @@ def delete_session(
.where(models.User.app_id == app_id)
.where(models.Session.user_id == user_id)
)
honcho_session = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
honcho_session = result.scalar_one_or_none()
if honcho_session is None:
return False
honcho_session.is_active = False
db.commit()
await db.commit()
return True
@ -250,8 +278,8 @@ def delete_session(
########################################################
def create_message(
db: Session,
async def create_message(
db: AsyncSession,
message: schemas.MessageCreate,
app_id: uuid.UUID,
user_id: uuid.UUID,
@ -267,19 +295,21 @@ def create_message(
session_id=session_id,
is_user=message.is_user,
content=message.content,
h_metadata=message.metadata,
)
db.add(honcho_message)
db.commit()
db.refresh(honcho_message)
await db.commit()
await db.refresh(honcho_message)
return honcho_message
def get_messages(
db: Session,
async def get_messages(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
reverse: Optional[bool] = False,
filter: Optional[dict] = None,
) -> Select:
stmt = (
select(models.Message)
@ -291,6 +321,9 @@ def get_messages(
.where(models.Message.session_id == session_id)
)
if filter is not None:
stmt = stmt.where(models.Message.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.Message.created_at.desc())
else:
@ -299,8 +332,8 @@ def get_messages(
return stmt
def get_message(
db: Session,
async def get_message(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
@ -316,7 +349,30 @@ def get_message(
.where(models.Message.session_id == session_id)
.where(models.Message.id == message_id)
)
return db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
return result.scalar_one_or_none()
async def update_message(
db: AsyncSession,
message: schemas.MessageUpdate,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
message_id: uuid.UUID,
) -> bool:
honcho_message = await get_message(
db, app_id=app_id, session_id=session_id, user_id=user_id, message_id=message_id
)
if honcho_message is None:
raise ValueError("Message not found or does not belong to user")
if (
message.metadata is not None
): # Need to explicitly be there won't make it empty by default
honcho_message.h_metadata = message.metadata
await db.commit()
await db.refresh(honcho_message)
return honcho_message
########################################################
@ -324,8 +380,8 @@ def get_message(
########################################################
def create_metamessage(
db: Session,
async def create_metamessage(
db: AsyncSession,
metamessage: schemas.MetamessageCreate,
app_id: uuid.UUID,
user_id: uuid.UUID,
@ -345,21 +401,23 @@ def create_metamessage(
message_id=metamessage.message_id,
metamessage_type=metamessage.metamessage_type,
content=metamessage.content,
h_metadata=metamessage.metadata,
)
db.add(honcho_metamessage)
db.commit()
db.refresh(honcho_metamessage)
await db.commit()
await db.refresh(honcho_metamessage)
return honcho_metamessage
def get_metamessages(
db: Session,
async def get_metamessages(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
message_id: Optional[uuid.UUID],
metamessage_type: Optional[str] = None,
filter: Optional[dict] = None,
reverse: Optional[bool] = False,
) -> Select:
stmt = (
@ -379,6 +437,9 @@ def get_metamessages(
if metamessage_type is not None:
stmt = stmt.where(models.Metamessage.metamessage_type == metamessage_type)
if filter is not None:
stmt = stmt.where(models.Metamessage.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.Metamessage.created_at.desc())
else:
@ -387,8 +448,8 @@ def get_metamessages(
return stmt
def get_metamessage(
db: Session,
async def get_metamessage(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
@ -407,7 +468,37 @@ def get_metamessage(
.where(models.Metamessage.message_id == message_id)
.where(models.Metamessage.id == metamessage_id)
)
return db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
return result.scalar_one_or_none()
async def update_metamessage(
db: AsyncSession,
metamessage: schemas.MetamessageUpdate,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
metamessage_id: uuid.UUID,
) -> bool:
honcho_metamessage = await get_metamessage(
db,
app_id=app_id,
session_id=session_id,
user_id=user_id,
message_id=metamessage.message_id,
metamessage_id=metamessage_id,
)
if honcho_metamessage is None:
raise ValueError("Metamessage not found or does not belong to user")
if (
metamessage.metadata is not None
): # Need to explicitly be there won't make it empty by default
honcho_metamessage.h_metadata = metamessage.metadata
if metamessage.metamessage_type is not None:
honcho_metamessage.metamessage_type = metamessage.metamessage_type
await db.commit()
await db.refresh(honcho_metamessage)
return honcho_metamessage
########################################################
@ -417,8 +508,12 @@ def get_metamessage(
# Should be very similar to the session methods
def get_collections(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, reverse: Optional[bool] = False
async def get_collections(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
reverse: Optional[bool] = False,
filter: Optional[dict] = None,
) -> Select:
"""Get a distinct list of the names of collections associated with a user"""
stmt = (
@ -428,6 +523,9 @@ def get_collections(
.where(models.User.id == user_id)
)
if filter is not None:
stmt = stmt.where(models.Collection.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.Collection.created_at.desc())
else:
@ -436,8 +534,8 @@ def get_collections(
return stmt
def get_collection_by_id(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, collection_id: uuid.UUID
async def get_collection_by_id(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID, collection_id: uuid.UUID
) -> Optional[models.Collection]:
stmt = (
select(models.Collection)
@ -446,12 +544,13 @@ def get_collection_by_id(
.where(models.User.id == user_id)
.where(models.Collection.id == collection_id)
)
collection = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
collection = result.scalar_one_or_none()
return collection
def get_collection_by_name(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, name: str
async def get_collection_by_name(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID, name: str
) -> Optional[models.Collection]:
stmt = (
select(models.Collection)
@ -460,12 +559,13 @@ def get_collection_by_name(
.where(models.User.id == user_id)
.where(models.Collection.name == name)
)
collection = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
collection = result.scalar_one_or_none()
return collection
def create_collection(
db: Session,
async def create_collection(
db: AsyncSession,
collection: schemas.CollectionCreate,
app_id: uuid.UUID,
user_id: uuid.UUID,
@ -473,41 +573,44 @@ def create_collection(
honcho_collection = models.Collection(
user_id=user_id,
name=collection.name,
h_metadata=collection.metadata,
)
try:
db.add(honcho_collection)
db.commit()
await db.commit()
except IntegrityError:
db.rollback()
raise ValueError("Collection already exists")
db.refresh(honcho_collection)
await db.rollback()
raise ValueError("Collection already exists") from None
await db.refresh(honcho_collection)
return honcho_collection
def update_collection(
db: Session,
async def update_collection(
db: AsyncSession,
collection: schemas.CollectionUpdate,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
) -> models.Collection:
honcho_collection = get_collection_by_id(
honcho_collection = await get_collection_by_id(
db, app_id=app_id, user_id=user_id, collection_id=collection_id
)
if honcho_collection is None:
raise ValueError("collection not found or does not belong to user")
if collection.metadata is not None:
honcho_collection.h_metadata = collection.metadata
try:
honcho_collection.name = collection.name
db.commit()
await db.commit()
except IntegrityError:
db.rollback()
raise ValueError("Collection already exists")
db.refresh(honcho_collection)
await db.rollback()
raise ValueError("Collection already exists") from None
await db.refresh(honcho_collection)
return honcho_collection
def delete_collection(
db: Session, app_id: uuid.UUID, user_id: uuid.UUID, collection_id: uuid.UUID
async def delete_collection(
db: AsyncSession, app_id: uuid.UUID, user_id: uuid.UUID, collection_id: uuid.UUID
) -> bool:
"""
Delete a Collection and all documents associated with it. Takes advantage of
@ -520,11 +623,12 @@ def delete_collection(
.where(models.User.id == user_id)
.where(models.Collection.id == collection_id)
)
honcho_collection = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
honcho_collection = result.scalar_one_or_none()
if honcho_collection is None:
return False
db.delete(honcho_collection)
db.commit()
await db.delete(honcho_collection)
await db.commit()
return True
@ -535,12 +639,13 @@ def delete_collection(
# Should be similar to the messages methods outside of query
def get_documents(
db: Session,
async def get_documents(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
reverse: Optional[bool] = False,
filter: Optional[dict] = None,
) -> Select:
stmt = (
select(models.Document)
@ -551,6 +656,9 @@ def get_documents(
.where(models.Document.collection_id == collection_id)
)
if filter is not None:
stmt = stmt.where(models.Document.h_metadata.contains(filter))
if reverse:
stmt = stmt.order_by(models.Document.created_at.desc())
else:
@ -559,8 +667,8 @@ def get_documents(
return stmt
def get_document(
db: Session,
async def get_document(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
@ -576,16 +684,18 @@ def get_document(
.where(models.Document.id == document_id)
)
document = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
document = result.scalar_one_or_none()
return document
def query_documents(
db: Session,
async def query_documents(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
query: str,
filter: Optional[dict] = None,
top_k: int = 5,
) -> Sequence[models.Document]:
response = openai_client.embeddings.create(
@ -599,23 +709,26 @@ def query_documents(
.where(models.User.app_id == app_id)
.where(models.User.id == user_id)
.where(models.Document.collection_id == collection_id)
.order_by(models.Document.embedding.cosine_distance(embedding_query))
.limit(top_k)
# .limit(top_k)
)
# if metadata is not None:
# stmt = stmt.where(models.Document.h_metadata.contains(metadata))
return db.scalars(stmt).all()
if filter is not None:
stmt = stmt.where(models.Document.h_metadata.contains(filter))
stmt = stmt.limit(top_k).order_by(
models.Document.embedding.cosine_distance(embedding_query)
)
result = await db.execute(stmt)
return result.scalars().all()
def create_document(
db: Session,
async def create_document(
db: AsyncSession,
document: schemas.DocumentCreate,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
) -> models.Document:
"""Embed a message as a vector and create a document"""
collection = get_collection_by_id(
collection = await get_collection_by_id(
db, app_id=app_id, collection_id=collection_id, user_id=user_id
)
if collection is None:
@ -634,20 +747,20 @@ def create_document(
embedding=embedding,
)
db.add(honcho_document)
db.commit()
db.refresh(honcho_document)
await db.commit()
await db.refresh(honcho_document)
return honcho_document
def update_document(
db: Session,
async def update_document(
db: AsyncSession,
document: schemas.DocumentUpdate,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
document_id: uuid.UUID,
) -> bool:
honcho_document = get_document(
honcho_document = await get_document(
db,
app_id=app_id,
collection_id=collection_id,
@ -667,13 +780,13 @@ def update_document(
if document.metadata is not None:
honcho_document.h_metadata = document.metadata
db.commit()
db.refresh(honcho_document)
await db.commit()
await db.refresh(honcho_document)
return honcho_document
def delete_document(
db: Session,
async def delete_document(
db: AsyncSession,
app_id: uuid.UUID,
user_id: uuid.UUID,
collection_id: uuid.UUID,
@ -688,9 +801,10 @@ def delete_document(
.where(models.Document.collection_id == collection_id)
.where(models.Document.id == document_id)
)
document = db.scalars(stmt).one_or_none()
result = await db.execute(stmt)
document = result.scalar_one_or_none()
if document is None:
return False
db.delete(document)
db.commit()
await db.delete(document)
await db.commit()
return True

View File

@ -1,20 +1,31 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from dotenv import load_dotenv
import os
from dotenv import load_dotenv
from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
load_dotenv()
connect_args = {}
if os.environ["DATABASE_TYPE"] == "sqlite": # https://fastapi.tiangolo.com/tutorial/sql-databases/#note
if (
os.environ["DATABASE_TYPE"] == "sqlite"
): # https://fastapi.tiangolo.com/tutorial/sql-databases/#note
connect_args = {"check_same_thread": False}
engine = create_engine(
engine = create_async_engine(
os.environ["CONNECTION_URI"], connect_args=connect_args, echo=True
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
SessionLocal = async_sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def scaffold_db():
"""Use a Sync Engine for scaffolding the database. DDL operations are unavailable
with Async Engines
"""
engine = create_engine(os.environ["CONNECTION_URI"], echo=True)
Base.metadata.create_all(bind=engine)
engine.dispose()

203
api/src/harvester.py Normal file
View File

@ -0,0 +1,203 @@
import asyncio
import os
import uuid
from typing import List
from dotenv import load_dotenv
from langchain_core.output_parsers import NumberedListOutputParser
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
load_prompt,
)
from langchain_openai import ChatOpenAI
from realtime.connection import Socket
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from . import crud, models, schemas
from .db import SessionLocal
load_dotenv()
SUPABASE_ID = os.getenv("SUPABASE_ID")
SUPABASE_API_KEY = os.getenv("SUPABASE_API_KEY")
llm = ChatOpenAI(model_name="gpt-4")
output_parser = NumberedListOutputParser()
SYSTEM_DERIVE_FACTS = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/derive_facts.yaml")
)
SYSTEM_CHECK_DUPS = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/check_dup_facts.yaml")
)
system_check_dups: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_CHECK_DUPS
)
system_derive_facts: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_DERIVE_FACTS
)
async def callback(payload):
# print(payload["record"]["is_user"])
# print(type(payload["record"]["is_user"]))
if payload["record"]["is_user"]: # Check if the message is from a user
session_id = payload["record"]["session_id"]
message_id = payload["record"]["id"]
content = payload["record"]["content"]
# Example of querying for a user_id based on session_id, adjust according to your schema
session: models.Session
user_id: uuid.UUID
app_id: uuid.UUID
async with SessionLocal() as db:
stmt = (
select(models.Session)
.join(models.Session.messages)
.where(models.Message.id == message_id)
.where(models.Session.id == session_id)
.options(selectinload(models.Session.user))
)
result = await db.execute(stmt)
session = result.scalars().one()
user = session.user
user_id = user.id
app_id = user.app_id
collection: models.Collection
async with SessionLocal() as db:
collection = await crud.get_collection_by_name(
db, app_id, user_id, "honcho"
)
if collection is None:
collection_create = schemas.CollectionCreate(name="honcho", metadata={})
collection = await crud.create_collection(
db,
collection=collection_create,
app_id=app_id,
user_id=user_id,
)
collection_id = collection.id
await process_user_message(
content, app_id, user_id, session_id, collection_id, message_id
)
return
async def process_user_message(
content: str,
app_id: uuid.UUID,
user_id: uuid.UUID,
session_id: uuid.UUID,
collection_id: uuid.UUID,
message_id: uuid.UUID,
):
# TODO get messages for the session
async with SessionLocal() as db:
messages_stmt = await crud.get_messages(
db=db, app_id=app_id, user_id=user_id, session_id=session_id, reverse=True
)
messages_stmt = messages_stmt.limit(10)
response = await db.execute(messages_stmt)
messages = response.scalars().all()
messages = messages[::-1]
contents = [m.content for m in messages]
# print(contents)
facts = await derive_facts(messages, content)
print("===================")
print(f"DERIVED FACTS: {facts}")
print("===================")
new_facts = await check_dups(app_id, user_id, collection_id, facts)
print("===================")
print(f"CHECKED FOR DUPLICATES: {new_facts}")
print("===================")
for fact in new_facts:
create_document = schemas.DocumentCreate(content=fact)
async with SessionLocal() as db:
doc = await crud.create_document(
db,
document=create_document,
app_id=app_id,
user_id=user_id,
collection_id=collection_id,
)
print(f"Returned Document: {doc}")
# doc = crud.create_document(content=fact)
# for fact in new_facts:
# session.create_metamessage(
# message=user_message, metamessage_type="fact", content=fact
# )
# print(f"Created fact: {fact}")
async def derive_facts(chat_history, input: str) -> List[str]:
"""Derive facts from the user input"""
fact_derivation = ChatPromptTemplate.from_messages([system_derive_facts])
chain = fact_derivation | llm
response = await chain.ainvoke(
{
"chat_history": [
(
"user: " + message.content
if message.is_user
else "ai: " + message.content
)
for message in chat_history
],
"user_input": input,
}
)
facts = output_parser.parse(response.content)
return facts
async def check_dups(
app_id: uuid.UUID, user_id: uuid.UUID, collection_id: uuid.UUID, facts: List[str]
):
"""Check that we're not storing duplicate facts"""
check_duplication = ChatPromptTemplate.from_messages([system_check_dups])
query = " ".join(facts)
result = None
async with SessionLocal() as db:
result = await crud.query_documents(
db=db,
app_id=app_id,
user_id=user_id,
collection_id=collection_id,
query=query,
top_k=10,
)
# result = collection.query(query=query, top_k=10)
existing_facts = [document.content for document in result]
print("===================")
print(f"Existing Facts {existing_facts}")
print("===================")
if len(existing_facts) == 0:
return facts
chain = check_duplication | llm
response = await chain.ainvoke({"existing_facts": existing_facts, "facts": facts})
new_facts = output_parser.parse(response.content)
print("===================")
print(f"New Facts {facts}")
print("===================")
return new_facts
if __name__ == "__main__":
URL = f"wss://{SUPABASE_ID}.supabase.co/realtime/v1/websocket?apikey={SUPABASE_API_KEY}&vsn=1.0.0"
# URL = f"ws://127.0.0.1:54321/realtime/v1/websocket?apikey={SUPABASE_API_KEY}" # For local Supabase
s = Socket(URL)
s.connect()
channel = s.set_channel("realtime:public:messages")
channel.join().on("INSERT", lambda payload: asyncio.create_task(callback(payload)))
s.listen()

File diff suppressed because it is too large Load Diff

View File

@ -87,6 +87,7 @@ class Message(Base):
session_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("sessions.id"), index=True)
is_user: Mapped[bool]
content: Mapped[str] = mapped_column(String(65535))
h_metadata: Mapped[dict] = mapped_column("metadata", ColumnType, default={})
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), default=datetime.datetime.utcnow
@ -111,6 +112,7 @@ class Metamessage(Base):
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), default=datetime.datetime.utcnow
)
h_metadata: Mapped[dict] = mapped_column("metadata", ColumnType, default={})
def __repr__(self) -> str:
return f"Metamessages(id={self.id}, message_id={self.message_id}, metamessage_type={self.metamessage_type}, content={self.content[10:]})"
@ -125,6 +127,7 @@ class Collection(Base):
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), default=datetime.datetime.utcnow
)
h_metadata: Mapped[dict] = mapped_column("metadata", ColumnType, default={})
documents = relationship(
"Document", back_populates="collection", cascade="all, delete, delete-orphan"
)

View File

@ -0,0 +1,11 @@
_type: prompt
input_variables:
["existing_facts", "facts"]
template: >
Your job is to compare the following two lists and keep only unique items:
Old: ```{existing_facts}```
New: ```{facts}```
Remove redundant information from the new list and output the remaining facts as a numbered list. If there's nothing to remove (i.e. the statements are sufficiently different), print "None".

View File

@ -0,0 +1,10 @@
_type: prompt
input_variables:
["chat_history", "user_input"]
template: >
You are tasked with deriving discrete facts about the user based on their input. The goal is to only extract absolute facts from the message, do not make inferences beyond the text provided.
chat history: ```{chat_history}```
user input: ```{user_input}```
Output the facts as a numbered list.

View File

@ -0,0 +1,11 @@
_type: prompt
input_variables:
["agent_input", "retrieved_facts"]
template: >
You are tasked with responding to the query based on the context provided.
---
query: {agent_input}
context: {retrieved_facts}
---
Provide a brief, matter-of-fact, and appropriate response to the query based on the context provided. If the context provided doesn't aid in addressing the query, return None.

View File

@ -1,7 +1,8 @@
from pydantic import BaseModel, validator
import datetime
import uuid
from pydantic import BaseModel, validator
class AppBase(BaseModel):
pass
@ -68,21 +69,37 @@ class User(UserBase):
class MessageBase(BaseModel):
content: str
is_user: bool
class MessageCreate(MessageBase):
pass
class MessageCreate(MessageBase):
content: str
is_user: bool
metadata: dict | None = {}
class MessageUpdate(MessageBase):
metadata: dict | None = None
class Message(MessageBase):
content: str
is_user: bool
session_id: uuid.UUID
id: uuid.UUID
h_metadata: dict
metadata: dict
created_at: datetime.datetime
@validator("metadata", pre=True, allow_reuse=True)
def fetch_h_metadata(cls, value, values):
if "h_metadata" in values:
return values["h_metadata"]
return {}
class Config:
from_attributes = True
schema_extra = {"exclude": ["h_metadata"]}
class SessionBase(BaseModel):
@ -120,21 +137,40 @@ class Session(SessionBase):
class MetamessageBase(BaseModel):
metamessage_type: str
content: str
pass
class MetamessageCreate(MetamessageBase):
metamessage_type: str
content: str
message_id: uuid.UUID
metadata: dict | None = {}
class MetamessageUpdate(MetamessageBase):
message_id: uuid.UUID
metamessage_type: str | None = None
metadata: dict | None = None
class Metamessage(MetamessageBase):
metamessage_type: str
content: str
id: uuid.UUID
message_id: uuid.UUID
h_metadata: dict
metadata: dict
created_at: datetime.datetime
@validator("metadata", pre=True, allow_reuse=True)
def fetch_h_metadata(cls, value, values):
if "h_metadata" in values:
return values["h_metadata"]
return {}
class Config:
from_attributes = True
schema_extra = {"exclude": ["h_metadata"]}
class CollectionBase(BaseModel):
@ -143,20 +179,31 @@ class CollectionBase(BaseModel):
class CollectionCreate(CollectionBase):
name: str
metadata: dict | None = {}
class CollectionUpdate(CollectionBase):
name: str
metadata: dict | None = None
class Collection(CollectionBase):
id: uuid.UUID
name: str
user_id: uuid.UUID
h_metadata: dict
metadata: dict
created_at: datetime.datetime
@validator("metadata", pre=True, allow_reuse=True)
def fetch_h_metadata(cls, value, values):
if "h_metadata" in values:
return values["h_metadata"]
return {}
class Config:
from_attributes = True
schema_extra = {"exclude": ["h_metadata"]}
class DocumentBase(BaseModel):
@ -189,3 +236,7 @@ class Document(DocumentBase):
class Config:
from_attributes = True
schema_extra = {"exclude": ["h_metadata"]}
class AgentChat(BaseModel):
content: str

View File

@ -2,15 +2,19 @@ from typing import List
from uuid import uuid4
from langchain.prompts import ChatPromptTemplate
from langchain.schema import AIMessage, HumanMessage, SystemMessage
from langchain_community.chat_models.fake import FakeListChatModel
from honcho import Honcho
from honcho.ext.langchain import langchain_message_converter
app_name = str(uuid4())
# honcho = Honcho(app_id=app_id, base_url="http://localhost:8000") # uncomment to use local
honcho = Honcho(app_name=app_name) # uses demo server at https://demo.honcho.dev
honcho = Honcho(
app_name=app_name, base_url="http://localhost:8000"
) # uncomment to use local
# honcho = Honcho(app_name=app_name) # uses demo server at https://demo.honcho.dev
honcho.initialize()
responses = ["Fake LLM Response :)"]
@ -24,14 +28,14 @@ user = honcho.create_user(user_name)
session = user.create_session()
def langchain_message_converter(messages: List):
new_messages = []
for message in messages:
if message.is_user:
new_messages.append(HumanMessage(content=message.content))
else:
new_messages.append(AIMessage(content=message.content))
return new_messages
# def langchain_message_converter(messages: List):
# new_messages = []
# for message in messages:
# if message.is_user:
# new_messages.append(HumanMessage(content=message.content))
# else:
# new_messages.append(AIMessage(content=message.content))
# return new_messages
def chat():

752
example/cli/poetry.lock generated
View File

@ -125,14 +125,14 @@ files = [
[[package]]
name = "anyio"
version = "4.2.0"
version = "4.3.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "dev"
category = "main"
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]
@ -166,21 +166,21 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
[[package]]
name = "certifi"
version = "2023.11.17"
version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
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]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7.0"
files = [
@ -278,14 +278,14 @@ files = [
[[package]]
name = "dataclasses-json"
version = "0.6.3"
version = "0.6.4"
description = "Easily serialize dataclasses to and from JSON."
category = "dev"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "dataclasses_json-0.6.3-py3-none-any.whl", hash = "sha256:4aeb343357997396f6bca1acae64e486c3a723d8f5c76301888abeccf0c45176"},
{file = "dataclasses_json-0.6.3.tar.gz", hash = "sha256:35cb40aae824736fdf959801356641836365219cfe14caeb115c39136f775d2a"},
{file = "dataclasses_json-0.6.4-py3-none-any.whl", hash = "sha256:f90578b8a3177f7552f4e1a6e535e84293cd5da421fcce0642d49c0d7bdf8df2"},
{file = "dataclasses_json-0.6.4.tar.gz", hash = "sha256:73696ebf24936560cca79a2430cbc4f3dd23ac7bf46ed17f38e5e5e7657a6377"},
]
[package.dependencies]
@ -451,23 +451,82 @@ files = [
docs = ["Sphinx", "furo"]
test = ["objgraph", "psutil"]
[[package]]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
[[package]]
name = "honcho-ai"
version = "0.0.1"
version = "0.0.4"
description = "Python Client SDK for Honcho"
category = "main"
optional = false
python-versions = "^3.10"
python-versions = "^3.9"
files = []
develop = true
[package.dependencies]
requests = "^2.31.0"
httpx = "^0.26.0"
[package.source]
type = "directory"
url = "../../sdk"
[[package]]
name = "httpcore"
version = "1.0.4"
description = "A minimal low-level HTTP client."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
]
[package.dependencies]
certifi = "*"
h11 = ">=0.13,<0.15"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
trio = ["trio (>=0.22.0,<0.25.0)"]
[[package]]
name = "httpx"
version = "0.26.0"
description = "The next generation HTTP client."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"},
{file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"},
]
[package.dependencies]
anyio = "*"
certifi = "*"
httpcore = ">=1.0.0,<2.0.0"
idna = "*"
sniffio = "*"
[package.extras]
brotli = ["brotli", "brotlicffi"]
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "idna"
version = "3.6"
@ -509,23 +568,24 @@ files = [
[[package]]
name = "langchain"
version = "0.1.4"
version = "0.1.11"
description = "Building applications with LLMs through composability"
category = "dev"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langchain-0.1.4-py3-none-any.whl", hash = "sha256:6befdd6221f5f326092e31a3c19efdc7ce3d7d1f2e2cab065141071451730ed7"},
{file = "langchain-0.1.4.tar.gz", hash = "sha256:8767a9461e2b717ce9a35b1fa20659de89ea86ba9c2a4ff516e05d47ab2d195d"},
{file = "langchain-0.1.11-py3-none-any.whl", hash = "sha256:b5e678ac50d85370b9bc28f2c97ad5f029aac1c0cca79cac9354adf72741bc6e"},
{file = "langchain-0.1.11.tar.gz", hash = "sha256:03f08cae7cd3f341c54f1042b3fe24d88f39eba7b7eda942735d8ced13fe6da9"},
]
[package.dependencies]
aiohttp = ">=3.8.3,<4.0.0"
dataclasses-json = ">=0.5.7,<0.7"
jsonpatch = ">=1.33,<2.0"
langchain-community = ">=0.0.14,<0.1"
langchain-core = ">=0.1.16,<0.2"
langsmith = ">=0.0.83,<0.1"
langchain-community = ">=0.0.25,<0.1"
langchain-core = ">=0.1.29,<0.2"
langchain-text-splitters = ">=0.0.1,<0.1"
langsmith = ">=0.1.17,<0.2.0"
numpy = ">=1,<2"
pydantic = ">=1,<3"
PyYAML = ">=5.3"
@ -534,13 +594,13 @@ SQLAlchemy = ">=1.4,<3"
tenacity = ">=8.1.0,<9.0.0"
[package.extras]
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-ai-vision (>=0.11.1b1,<0.12.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
clarifai = ["clarifai (>=9.1.0)"]
cli = ["typer (>=0.9.0,<0.10.0)"]
cohere = ["cohere (>=4,<5)"]
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
embeddings = ["sentence-transformers (>=2,<3)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.0.2,<0.1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.0.2,<0.1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
javascript = ["esprima (>=4.0.1,<5.0.0)"]
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<5)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
openai = ["openai (<2)", "tiktoken (>=0.3.2,<0.6.0)"]
@ -549,21 +609,21 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
[[package]]
name = "langchain-community"
version = "0.0.16"
version = "0.0.28"
description = "Community contributed LangChain integrations."
category = "dev"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langchain_community-0.0.16-py3-none-any.whl", hash = "sha256:0f1dfc1a6205ce8d39931d3515974a208a9f69c16157c649f83490a7cc830b73"},
{file = "langchain_community-0.0.16.tar.gz", hash = "sha256:c06512a93013a06fba7679cd5a1254ff8b927cddd2d1fbe0cc444bf7bbdf0b8c"},
{file = "langchain_community-0.0.28-py3-none-any.whl", hash = "sha256:bdb015ac455ae68432ea104628717583dce041e1abdfcefe86e39f034f5e90b8"},
{file = "langchain_community-0.0.28.tar.gz", hash = "sha256:8664d243a90550fc5ddc137b712034e02c8d43afc8d4cc832ba5842b44c864ce"},
]
[package.dependencies]
aiohttp = ">=3.8.3,<4.0.0"
dataclasses-json = ">=0.5.7,<0.7"
langchain-core = ">=0.1.16,<0.2"
langsmith = ">=0.0.83,<0.1"
langchain-core = ">=0.1.31,<0.2.0"
langsmith = ">=0.1.0,<0.2.0"
numpy = ">=1,<2"
PyYAML = ">=5.3"
requests = ">=2,<3"
@ -572,24 +632,24 @@ tenacity = ">=8.1.0,<9.0.0"
[package.extras]
cli = ["typer (>=0.9.0,<0.10.0)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)", "zhipuai (>=1.0.7,<2.0.0)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)", "zhipuai (>=1.0.7,<2.0.0)"]
[[package]]
name = "langchain-core"
version = "0.1.17"
version = "0.1.31"
description = "Building applications with LLMs through composability"
category = "dev"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langchain_core-0.1.17-py3-none-any.whl", hash = "sha256:026155cf97867bde410ab1834799ab4c5ba64c39380f2a4328bcf9c78623ca64"},
{file = "langchain_core-0.1.17.tar.gz", hash = "sha256:59016e457cd6a1708d83a3a454acc97cf02c2a2c3af95626d13f83894fd4e777"},
{file = "langchain_core-0.1.31-py3-none-any.whl", hash = "sha256:ff028f00db8ff03565b542cea81be27426022a72c6545b54d8de66fa00948ab3"},
{file = "langchain_core-0.1.31.tar.gz", hash = "sha256:d660cf209bb6ce61cb1c853107b091aaa809015a55dce9e0ce19b51d4c8f2a70"},
]
[package.dependencies]
anyio = ">=3,<5"
jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.0.83,<0.1"
langsmith = ">=0.1.0,<0.2.0"
packaging = ">=23.2,<24.0"
pydantic = ">=1,<3"
PyYAML = ">=5.3"
@ -599,125 +659,159 @@ tenacity = ">=8.1.0,<9.0.0"
[package.extras]
extended-testing = ["jinja2 (>=3,<4)"]
[[package]]
name = "langchain-text-splitters"
version = "0.0.1"
description = "LangChain text splitting utilities"
category = "dev"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langchain_text_splitters-0.0.1-py3-none-any.whl", hash = "sha256:f5b802f873f5ff6a8b9259ff34d53ed989666ef4e1582e6d1adb3b5520e3839a"},
{file = "langchain_text_splitters-0.0.1.tar.gz", hash = "sha256:ac459fa98799f5117ad5425a9330b21961321e30bc19a2a2f9f761ddadd62aa1"},
]
[package.dependencies]
langchain-core = ">=0.1.28,<0.2.0"
[package.extras]
extended-testing = ["lxml (>=5.1.0,<6.0.0)"]
[[package]]
name = "langsmith"
version = "0.0.85"
version = "0.1.24"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
category = "dev"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langsmith-0.0.85-py3-none-any.whl", hash = "sha256:9d0ccbcda7b69c83828060603a51bb4319e43b8dc807fbd90b6355f8ec709500"},
{file = "langsmith-0.0.85.tar.gz", hash = "sha256:fefc631fc30d836b54d4e3f99961c41aea497633898b8f09e305b6c7216c2c54"},
{file = "langsmith-0.1.24-py3-none-any.whl", hash = "sha256:898ef5265bca8fc912f7fbf207e1d69cacd86055faecf6811bd42641e6319840"},
{file = "langsmith-0.1.24.tar.gz", hash = "sha256:432b829e763f5077df411bc59bb35449813f18174d2ebc8bbbb38427071d5e7d"},
]
[package.dependencies]
orjson = ">=3.9.14,<4.0.0"
pydantic = ">=1,<3"
requests = ">=2,<3"
[[package]]
name = "marshmallow"
version = "3.20.2"
version = "3.21.1"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"},
{file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"},
{file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"},
{file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"},
]
[package.dependencies]
packaging = ">=17.0"
[package.extras]
dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"]
docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
lint = ["pre-commit (>=2.4,<4.0)"]
dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
name = "multidict"
version = "6.0.4"
version = "6.0.5"
description = "multidict implementation"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
{file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
{file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
{file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
{file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
{file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
{file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
{file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
{file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
{file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
{file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
{file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
{file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
{file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
{file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
{file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
{file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
{file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
{file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
{file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
{file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
{file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
{file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
{file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
{file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
{file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
{file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
{file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
{file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
{file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
{file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
{file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
{file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
{file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
{file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
{file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
{file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
{file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
{file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
{file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
{file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"},
{file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"},
{file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"},
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"},
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"},
{file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"},
{file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"},
{file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"},
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
{file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
{file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
{file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
{file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
{file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
]
[[package]]
@ -734,48 +828,108 @@ files = [
[[package]]
name = "numpy"
version = "1.26.3"
version = "1.26.4"
description = "Fundamental package for array computing in Python"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"},
{file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"},
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"},
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"},
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"},
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"},
{file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"},
{file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"},
{file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"},
{file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"},
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"},
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"},
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"},
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"},
{file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"},
{file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"},
{file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"},
{file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"},
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"},
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"},
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"},
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"},
{file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"},
{file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"},
{file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"},
{file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"},
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"},
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"},
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"},
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"},
{file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"},
{file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"},
{file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"},
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
{file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
{file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
{file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
{file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
{file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
{file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
{file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
{file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
{file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
{file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
{file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
{file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
{file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
{file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
{file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
]
[[package]]
name = "orjson"
version = "3.9.15"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"},
{file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"},
{file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"},
{file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"},
{file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"},
{file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"},
{file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"},
{file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"},
{file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"},
{file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"},
{file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"},
{file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"},
{file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"},
{file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"},
{file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"},
{file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"},
{file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"},
{file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"},
{file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"},
{file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"},
{file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"},
{file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"},
{file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"},
{file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"},
{file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"},
{file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"},
{file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"},
{file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"},
{file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"},
{file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"},
{file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"},
{file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"},
{file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"},
{file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"},
{file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"},
{file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"},
{file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"},
{file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"},
{file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"},
{file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"},
{file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"},
{file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"},
{file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"},
{file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"},
{file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"},
{file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"},
{file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"},
{file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"},
{file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"},
{file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"},
]
[[package]]
@ -792,19 +946,19 @@ files = [
[[package]]
name = "pydantic"
version = "2.6.0"
version = "2.6.4"
description = "Data validation using Python type hints"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"},
{file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"},
{file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"},
{file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"},
]
[package.dependencies]
annotated-types = ">=0.4.0"
pydantic-core = "2.16.1"
pydantic-core = "2.16.3"
typing-extensions = ">=4.6.1"
[package.extras]
@ -812,91 +966,91 @@ email = ["email-validator (>=2.0.0)"]
[[package]]
name = "pydantic-core"
version = "2.16.1"
version = "2.16.3"
description = ""
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"},
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"},
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"},
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"},
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"},
{file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"},
{file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"},
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"},
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"},
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"},
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"},
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"},
{file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"},
{file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"},
{file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"},
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"},
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"},
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"},
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"},
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"},
{file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"},
{file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"},
{file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"},
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"},
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"},
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"},
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"},
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"},
{file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"},
{file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"},
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"},
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"},
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"},
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"},
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"},
{file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"},
{file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"},
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"},
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"},
{file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"},
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
{file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
{file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
{file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
{file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
{file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
{file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
{file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
{file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
{file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
{file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
{file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
{file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
{file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
]
[package.dependencies]
@ -956,7 +1110,7 @@ files = [
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -976,73 +1130,73 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "sniffio"
version = "1.3.0"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
category = "dev"
category = "main"
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]]
name = "sqlalchemy"
version = "2.0.25"
version = "2.0.28"
description = "Database Abstraction Library"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"},
{file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"},
{file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"},
{file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"},
{file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"},
{file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"},
{file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"},
{file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"},
{file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"},
{file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"},
{file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"},
]
[package.dependencies]
@ -1091,14 +1245,14 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
[[package]]
name = "typing-extensions"
version = "4.9.0"
version = "4.10.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
category = "dev"
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]]
@ -1119,14 +1273,14 @@ typing-extensions = ">=3.7.4"
[[package]]
name = "urllib3"
version = "2.2.0"
version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"},
{file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"},
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
]
[package.extras]

View File

@ -2,9 +2,9 @@ import os
from uuid import uuid1
import discord
from honcho import Honcho
from honcho.ext.langchain import langchain_message_converter
from graph import chat
from dspy import Example
from chain import langchain_message_converter
intents = discord.Intents.default()
intents.messages = True
@ -51,7 +51,9 @@ async def on_message(message):
user = honcho.get_or_create_user(user_id)
location_id = str(message.channel.id)
sessions = list(user.get_sessions_generator(location_id, is_active=True, reverse=True))
sessions = list(
user.get_sessions_generator(location_id, is_active=True, reverse=True)
)
if len(sessions) > 0:
session = sessions[0]
@ -86,7 +88,9 @@ async def on_reaction_add(reaction, user):
honcho_user = honcho.get_or_create_user(user_id)
location_id = str(reaction.message.channel.id)
sessions = list(honcho_user.get_sessions_generator(location_id, is_active=True, reverse=True))
sessions = list(
honcho_user.get_sessions_generator(location_id, is_active=True, reverse=True)
)
if len(sessions) > 0:
session = sessions[0]
else:
@ -100,25 +104,29 @@ async def on_reaction_add(reaction, user):
user_response = user_responses[0]
user_state_storage = dict(honcho_user.metadata)
user_state = list(session.get_metamessages_generator(metamessage_type="user_state", message=user_response, reverse=True))[0].content
user_state = list(
session.get_metamessages_generator(
metamessage_type="user_state", message=user_response, reverse=True
)
)[0].content
examples = user_state_storage[user_state]["examples"]
# Check if the reaction is a thumbs up
if str(reaction.emoji) == "👍":
example = Example(
chat_input=user_response.content,
chat_input=user_response.content,
response=ai_response,
assessment_dimension=user_state,
label='yes'
label="yes",
).with_inputs("chat_input", "response", "assessment_dimension")
examples.append(example.toDict())
# Check if the reaction is a thumbs down
elif str(reaction.emoji) == "👎":
example = Example(
chat_input=user_response.content,
chat_input=user_response.content,
response=ai_response,
assessment_dimension=user_state,
label='no'
label="no",
).with_inputs("chat_input", "response", "assessment_dimension")
examples.append(example.toDict())

View File

@ -2,7 +2,11 @@ import os
from typing import List, Union
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, load_prompt
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
load_prompt,
)
from langchain_core.messages import AIMessage, HumanMessage
from honcho import Message
@ -10,84 +14,103 @@ from honcho import Message
load_dotenv()
# langchain prompts
SYSTEM_STATE_COMMENTARY = load_prompt(os.path.join(os.path.dirname(__file__), 'langchain_prompts/state_commentary.yaml'))
SYSTEM_STATE_LABELING = load_prompt(os.path.join(os.path.dirname(__file__), 'langchain_prompts/state_labeling.yaml'))
SYSTEM_STATE_CHECK = load_prompt(os.path.join(os.path.dirname(__file__), 'langchain_prompts/state_check.yaml'))
# quick utility function to convert messages from honcho to langchain
def langchain_message_converter(messages: List[Message]) -> List[Union[AIMessage, HumanMessage]]:
new_messages = []
for message in messages:
if message.is_user:
new_messages.append(HumanMessage(content=message.content))
else:
new_messages.append(AIMessage(content=message.content))
return new_messages
SYSTEM_STATE_COMMENTARY = load_prompt(
os.path.join(os.path.dirname(__file__), "langchain_prompts/state_commentary.yaml")
)
SYSTEM_STATE_LABELING = load_prompt(
os.path.join(os.path.dirname(__file__), "langchain_prompts/state_labeling.yaml")
)
SYSTEM_STATE_CHECK = load_prompt(
os.path.join(os.path.dirname(__file__), "langchain_prompts/state_check.yaml")
)
# convert chat history and user input into a string
def format_chat_history(chat_history: List[Message], user_input=None):
messages = [("user: " + message.content if isinstance(message, HumanMessage) else "ai: " + message.content) for message in chat_history]
messages = [
(
"user: " + message.content
if isinstance(message, HumanMessage)
else "ai: " + message.content
)
for message in chat_history
]
if user_input:
messages.append(f"user: {user_input}")
return "\n".join(messages)
class StateExtractor:
"""Wrapper class for all the DSPy and LangChain code for user state labeling and pipeline optimization"""
lc_gpt_4: ChatOpenAI = ChatOpenAI(model_name = "gpt-4")
lc_gpt_turbo: ChatOpenAI = ChatOpenAI(model_name = "gpt-3.5-turbo")
system_state_commentary: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_STATE_COMMENTARY)
system_state_labeling: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_STATE_LABELING)
system_state_check: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_STATE_CHECK)
lc_gpt_4: ChatOpenAI = ChatOpenAI(model_name="gpt-4")
lc_gpt_turbo: ChatOpenAI = ChatOpenAI(model_name="gpt-3.5-turbo")
system_state_commentary: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_STATE_COMMENTARY
)
system_state_labeling: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_STATE_LABELING
)
system_state_check: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_STATE_CHECK
)
def __init__(self) -> None:
pass
@classmethod
async def generate_state_commentary(cls, existing_states: List[str], chat_history: List[Message], input: str) -> str:
async def generate_state_commentary(
cls, existing_states: List[str], chat_history: List[Message], input: str
) -> str:
"""Generate a commentary on the current state of the user"""
# format existing states
existing_states = "\n".join(existing_states)
# format prompt
state_commentary = ChatPromptTemplate.from_messages([
cls.system_state_commentary
])
state_commentary = ChatPromptTemplate.from_messages(
[cls.system_state_commentary]
)
# LCEL
chain = state_commentary | cls.lc_gpt_4
# inference
response = await chain.ainvoke({
"chat_history": chat_history,
"user_input": input,
"existing_states": existing_states,
})
response = await chain.ainvoke(
{
"chat_history": chat_history,
"user_input": input,
"existing_states": existing_states,
}
)
# return output
return response.content
@classmethod
async def generate_state_label(cls, existing_states: List[str], state_commentary: str) -> str:
async def generate_state_label(
cls, existing_states: List[str], state_commentary: str
) -> str:
"""Generate a state label from a commetary on the user's state"""
# format existing states
existing_states = "\n".join(existing_states)
# format prompt
state_labeling = ChatPromptTemplate.from_messages([
cls.system_state_labeling,
])
state_labeling = ChatPromptTemplate.from_messages(
[
cls.system_state_labeling,
]
)
# LCEL
chain = state_labeling | cls.lc_gpt_4
# inference
response = await chain.ainvoke({
"state_commentary": state_commentary,
"existing_states": existing_states,
})
response = await chain.ainvoke(
{
"state_commentary": state_commentary,
"existing_states": existing_states,
}
)
# strip anything that's not letters
clean_response = ''.join(c for c in response.content if c.isalpha())
clean_response = "".join(c for c in response.content if c.isalpha())
# return output
return clean_response
@classmethod
async def check_state_exists(cls, existing_states: List[str], state: str):
"""Check if a user state is new or already is stored"""
@ -96,32 +119,36 @@ class StateExtractor:
existing_states = "\n".join(existing_states)
# format prompt
state_check = ChatPromptTemplate.from_messages([
cls.system_state_check
])
state_check = ChatPromptTemplate.from_messages([cls.system_state_check])
# LCEL
chain = state_check | cls.lc_gpt_turbo
# inference
response = await chain.ainvoke({
"existing_states": existing_states,
"state": state,
})
response = await chain.ainvoke(
{
"existing_states": existing_states,
"state": state,
}
)
# return output
return response.content
@classmethod
async def generate_state(cls, existing_states: List[str], chat_history: List[Message], input: str):
""""Determine the user's state from the current conversation state"""
async def generate_state(
cls, existing_states: List[str], chat_history: List[Message], input: str
):
""" "Determine the user's state from the current conversation state"""
# Generate label
state_commentary = await cls.generate_state_commentary(existing_states, chat_history, input)
state_commentary = await cls.generate_state_commentary(
existing_states, chat_history, input
)
state_label = await cls.generate_state_label(existing_states, state_commentary)
# Determine if state is new
# if True, it doesn't exist, state is new
# if False, it does exist, state is not new, existing_state was returned
existing_state = await cls.check_state_exists(existing_states, state_label)
is_state_new = existing_state == "None"
is_state_new = existing_state == "None"
# return existing state if we found one
if is_state_new:

View File

@ -2,7 +2,8 @@ import os
from uuid import uuid1
import discord
from honcho import Honcho
from chain import langchain_message_converter, LMChain
from honcho.ext.langchain import langchain_message_converter
from chain import LMChain
intents = discord.Intents.default()

View File

@ -2,7 +2,11 @@ import os
from typing import List
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, load_prompt
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
load_prompt,
)
from langchain_core.output_parsers import NumberedListOutputParser
from langchain_core.messages import AIMessage, HumanMessage
@ -10,30 +14,37 @@ from honcho import Collection, Session, Message
load_dotenv()
SYSTEM_DERIVE_FACTS = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/core/derive_facts.yaml'))
SYSTEM_INTROSPECTION = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/core/introspection.yaml'))
SYSTEM_RESPONSE = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/core/response.yaml'))
SYSTEM_CHECK_DUPS = load_prompt(os.path.join(os.path.dirname(__file__), 'prompts/utils/check_dup_facts.yaml'))
def langchain_message_converter(messages: List):
new_messages = []
for message in messages:
if message.is_user:
new_messages.append(HumanMessage(content=message.content))
else:
new_messages.append(AIMessage(content=message.content))
return new_messages
SYSTEM_DERIVE_FACTS = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/core/derive_facts.yaml")
)
SYSTEM_INTROSPECTION = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/core/introspection.yaml")
)
SYSTEM_RESPONSE = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/core/response.yaml")
)
SYSTEM_CHECK_DUPS = load_prompt(
os.path.join(os.path.dirname(__file__), "prompts/utils/check_dup_facts.yaml")
)
class LMChain:
"Wrapper class for encapsulating the multiple different chains used"
output_parser = NumberedListOutputParser()
llm: ChatOpenAI = ChatOpenAI(model_name = "gpt-3.5-turbo")
system_derive_facts: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_DERIVE_FACTS)
system_introspection: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_INTROSPECTION)
system_response: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_RESPONSE)
system_check_dups: SystemMessagePromptTemplate = SystemMessagePromptTemplate(prompt=SYSTEM_CHECK_DUPS)
llm: ChatOpenAI = ChatOpenAI(model_name="gpt-3.5-turbo")
system_derive_facts: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_DERIVE_FACTS
)
system_introspection: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_INTROSPECTION
)
system_response: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_RESPONSE
)
system_check_dups: SystemMessagePromptTemplate = SystemMessagePromptTemplate(
prompt=SYSTEM_CHECK_DUPS
)
def __init__(self) -> None:
pass
@ -43,18 +54,25 @@ class LMChain:
"""Derive facts from the user input"""
# format prompt
fact_derivation = ChatPromptTemplate.from_messages([
cls.system_derive_facts
])
fact_derivation = ChatPromptTemplate.from_messages([cls.system_derive_facts])
# LCEL
chain = fact_derivation | cls.llm
# inference
response = await chain.ainvoke({
"chat_history": [("user: " + message.content if isinstance(message, HumanMessage) else "ai: " + message.content) for message in chat_history],
"user_input": input
})
response = await chain.ainvoke(
{
"chat_history": [
(
"user: " + message.content
if isinstance(message, HumanMessage)
else "ai: " + message.content
)
for message in chat_history
],
"user_input": input,
}
)
# parse output
facts = cls.output_parser.parse(response.content)
@ -62,28 +80,31 @@ class LMChain:
print(f"DERIVED FACTS: {facts}")
return facts
@classmethod
async def check_dups(cls, user_message: Message, session: Session, collection: Collection, facts: List):
async def check_dups(
cls,
user_message: Message,
session: Session,
collection: Collection,
facts: List,
):
"""Check that we're not storing duplicate facts"""
# format prompt
check_duplication = ChatPromptTemplate.from_messages([
cls.system_check_dups
])
check_duplication = ChatPromptTemplate.from_messages([cls.system_check_dups])
query = " ".join(facts)
result = collection.query(query=query, top_k=10)
result = collection.query(query=query, top_k=10)
existing_facts = [document.content for document in result]
# LCEL
chain = check_duplication | cls.llm
# inference
response = await chain.ainvoke({
"existing_facts": existing_facts,
"facts": facts
})
response = await chain.ainvoke(
{"existing_facts": existing_facts, "facts": facts}
)
# parse output
new_facts = cls.output_parser.parse(response.content)
@ -96,28 +117,30 @@ class LMChain:
# add facts as metamessages
for fact in new_facts:
session.create_metamessage(message=user_message, metamessage_type="fact", content=fact)
session.create_metamessage(
message=user_message, metamessage_type="fact", content=fact
)
return
@classmethod
async def introspect(cls, user_message: Message, session: Session, chat_history: List, input:str):
async def introspect(
cls, user_message: Message, session: Session, chat_history: List, input: str
):
"""Generate questions about the user to use as retrieval over the fact store"""
# format prompt
introspection_prompt = ChatPromptTemplate.from_messages([
cls.system_introspection
])
introspection_prompt = ChatPromptTemplate.from_messages(
[cls.system_introspection]
)
# LCEL
chain = introspection_prompt | cls.llm
# inference
response = await chain.ainvoke({
"chat_history": chat_history,
"user_input": input
})
response = await chain.ainvoke(
{"chat_history": chat_history, "user_input": input}
)
# parse output
questions = cls.output_parser.parse(response.content)
@ -126,21 +149,22 @@ class LMChain:
# write questions as metamessages
for question in questions:
session.create_metamessage(message=user_message, metamessage_type="introspect", content=question)
session.create_metamessage(
message=user_message, metamessage_type="introspect", content=question
)
return questions
@classmethod
async def respond(cls, collection: Collection, chat_history: List, questions: List, input: str):
async def respond(
cls, collection: Collection, chat_history: List, questions: List, input: str
):
"""Take the facts and chat history and generate a personalized response"""
# format prompt
response_prompt = ChatPromptTemplate.from_messages([
cls.system_response,
*chat_history,
HumanMessage(content=input)
])
response_prompt = ChatPromptTemplate.from_messages(
[cls.system_response, *chat_history, HumanMessage(content=input)]
)
retrieved_facts = collection.query(query=questions, top_k=10)
retrieved_facts_content = [document.content for document in retrieved_facts]
@ -149,18 +173,29 @@ class LMChain:
chain = response_prompt | cls.llm
# inference
response = await chain.ainvoke({
"facts": retrieved_facts_content,
})
response = await chain.ainvoke(
{
"facts": retrieved_facts_content,
}
)
return response.content
@classmethod
async def chat(cls, chat_history: List, user_message: Message, session: Session, collection: Collection, input: str):
async def chat(
cls,
chat_history: List,
user_message: Message,
session: Session,
collection: Collection,
input: str,
):
"""Chat with the model"""
facts = await cls.derive_facts(chat_history, input)
await cls.check_dups(user_message, session, collection, facts) if facts is not None else None
await cls.check_dups(
user_message, session, collection, facts
) if facts is not None else None
# introspect
questions = await cls.introspect(user_message, session, chat_history, input)
@ -169,7 +204,3 @@ class LMChain:
response = await cls.respond(collection, chat_history, questions, input)
return response

View File

@ -11,6 +11,7 @@ from langchain_core.output_parsers import StrOutputParser
from langchain_core.messages import AIMessage, HumanMessage
from honcho import Honcho
from honcho.ext.langchain import langchain_message_converter
load_dotenv()
@ -43,16 +44,6 @@ output_parser = StrOutputParser()
chain = prompt | model | output_parser
def langchain_message_converter(messages: List):
new_messages = []
for message in messages:
if message.is_user:
new_messages.append(HumanMessage(content=message.content))
else:
new_messages.append(AIMessage(content=message.content))
return new_messages
@bot.event
async def on_ready():
print(f"We have logged in as {bot.user}")

2
scripts/syncronizer.py Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
import os
import re

View File

@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.0.5] — 2024-03-14
### Added
* Metadata to all data primitives (Users, Sessions, Messages, etc.)
* Ability to filter paginated GET requests by JSON filter based on metadata
* Dialectic API to interact with honcho agent and get insights about users
* Code Coverage Tests
* Autogenerated Sphinx Documentation for Honcho Client SDK
* Built-in Langchain message converter
### Fixed
* URL encoding all GET requests in honcho client
## [0.0.4] — 2024-02-22
### Added

View File

@ -5,7 +5,8 @@ applications.
Read about the motivation of this project [here](https://blog.plasticlabs.ai).
Read the full documentation of this project [here](https://docs.honcho.dev)
Read the full documentation of this project [here](https://docs.honcho.dev) and
find the SDK reference [here](https://api.python.honcho.dev)
## Installation

20
sdk/docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= poetry run sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

41
sdk/docs/conf.py Normal file
View File

@ -0,0 +1,41 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import os
import sys
project = "Honcho"
copyright = "2024, Plastic Labs"
author = "Plastic Labs"
sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../honcho"))
)
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]
autodoc_member_order = "bysource"
autosummary_generate = True
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
viewcode_import = "import honcho"
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "furo"
html_static_path = ["_static"]

33
sdk/docs/index.rst Normal file
View File

@ -0,0 +1,33 @@
.. Honcho documentation master file, created by
sphinx-quickstart on Wed Mar 13 11:35:31 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Honcho's documentation!
==================================
.. .. automodule:: honcho.client
.. :members:
.. .. automodule:: honcho.sync_client
.. :members:
.. .. automodule:: honcho.ext.langchain
.. :members:
.. .. autosummary::
.. :toctree: _autosummary
.. :recursive:
.. toctree::
:maxdepth: 3
:caption: Contents:
source/modules
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

35
sdk/docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@ -0,0 +1,21 @@
honcho.ext package
==================
Submodules
----------
honcho.ext.langchain module
---------------------------
.. automodule:: honcho.ext.langchain
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: honcho.ext
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,53 @@
honcho package
==============
Subpackages
-----------
.. toctree::
:maxdepth: 4
honcho.ext
Submodules
----------
honcho.cache module
-------------------
.. automodule:: honcho.cache
:members:
:undoc-members:
:show-inheritance:
honcho.client module
--------------------
.. automodule:: honcho.client
:members:
:undoc-members:
:show-inheritance:
honcho.schemas module
---------------------
.. automodule:: honcho.schemas
:members:
:undoc-members:
:show-inheritance:
honcho.sync\_client module
--------------------------
.. automodule:: honcho.sync_client
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: honcho
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
honcho
======
.. toctree::
:maxdepth: 4
honcho

View File

@ -1,6 +1,7 @@
from __future__ import annotations
import datetime
import json
import uuid
from typing import Optional
@ -32,7 +33,13 @@ class AsyncGetPage:
class AsyncGetUserPage(AsyncGetPage):
"""Paginated Results for Get User Requests"""
def __init__(self, response: dict, honcho: AsyncHoncho, reverse: bool):
def __init__(
self,
response: dict,
honcho: AsyncHoncho,
filter: Optional[dict],
reverse: bool,
):
"""Constructor for Page Result from User Get Request
Args:
@ -42,6 +49,7 @@ class AsyncGetUserPage(AsyncGetPage):
"""
super().__init__(response)
self.honcho = honcho
self.filter = filter
self.reverse = reverse
self.items = [
AsyncUser(
@ -57,7 +65,10 @@ class AsyncGetUserPage(AsyncGetPage):
if self.page >= self.pages:
return None
return await self.honcho.get_users(
page=(self.page + 1), page_size=self.page_size, reverse=self.reverse
filter=self.filter,
page=(self.page + 1),
page_size=self.page_size,
reverse=self.reverse,
)
@ -70,6 +81,7 @@ class AsyncGetSessionPage(AsyncGetPage):
user: AsyncUser,
reverse: bool,
location_id: Optional[str],
filter: Optional[dict],
is_active: bool,
):
"""Constructor for Page Result from Session Get Request
@ -85,6 +97,7 @@ class AsyncGetSessionPage(AsyncGetPage):
self.location_id = location_id
self.reverse = reverse
self.is_active = is_active
self.filter = filter
self.items = [
AsyncSession(
user=user,
@ -99,14 +112,15 @@ class AsyncGetSessionPage(AsyncGetPage):
async def next(self):
"""Get the next page of results
Returns:
AsyncGetSessionPage | None: Next Page of Results or None if there
are no more sessions to retreive from a query
AsyncGetSessionPage | None: Next Page of Results or None if there are no more sessions to retreive from a query
"""
if self.page >= self.pages:
return None
return await self.user.get_sessions(
location_id=self.location_id,
filter=self.filter,
page=(self.page + 1),
page_size=self.page_size,
reverse=self.reverse,
@ -117,7 +131,13 @@ class AsyncGetSessionPage(AsyncGetPage):
class AsyncGetMessagePage(AsyncGetPage):
"""Paginated Results for Get Session Requests"""
def __init__(self, response: dict, session: AsyncSession, reverse: bool):
def __init__(
self,
response: dict,
session: AsyncSession,
filter: Optional[dict],
reverse: bool,
):
"""Constructor for Page Result from Session Get Request
Args:
@ -127,6 +147,7 @@ class AsyncGetMessagePage(AsyncGetPage):
"""
super().__init__(response)
self.session = session
self.filter = filter
self.reverse = reverse
self.items = [
Message(
@ -134,6 +155,7 @@ class AsyncGetMessagePage(AsyncGetPage):
id=message["id"],
is_user=message["is_user"],
content=message["content"],
metadata=message["metadata"],
created_at=message["created_at"],
)
for message in response["items"]
@ -141,6 +163,7 @@ class AsyncGetMessagePage(AsyncGetPage):
async def next(self):
"""Get the next page of results
Returns:
AsyncGetMessagePage | None: Next Page of Results or None if there
are no more messages to retreive from a query
@ -148,7 +171,7 @@ class AsyncGetMessagePage(AsyncGetPage):
if self.page >= self.pages:
return None
return await self.session.get_messages(
(self.page + 1), self.page_size, self.reverse
self.filter, (self.page + 1), self.page_size, self.reverse
)
@ -157,6 +180,7 @@ class AsyncGetMetamessagePage(AsyncGetPage):
self,
response: dict,
session,
filter: Optional[dict],
reverse: bool,
message_id: Optional[uuid.UUID],
metamessage_type: Optional[str],
@ -175,11 +199,13 @@ class AsyncGetMetamessagePage(AsyncGetPage):
self.session = session
self.message_id = message_id
self.metamessage_type = metamessage_type
self.filter = filter
self.reverse = reverse
self.items = [
Metamessage(
id=metamessage["id"],
message_id=metamessage["message_id"],
metadata=metamessage["metadata"],
metamessage_type=metamessage["metamessage_type"],
content=metamessage["content"],
created_at=metamessage["created_at"],
@ -189,6 +215,7 @@ class AsyncGetMetamessagePage(AsyncGetPage):
async def next(self):
"""Get the next page of results
Returns:
AsyncGetMetamessagePage | None: Next Page of Results or None if
there are no more metamessages to retreive from a query
@ -197,6 +224,7 @@ class AsyncGetMetamessagePage(AsyncGetPage):
return None
return await self.session.get_metamessages(
metamessage_type=self.metamessage_type,
filter=self.filter,
message=self.message_id,
page=(self.page + 1),
page_size=self.page_size,
@ -207,7 +235,9 @@ class AsyncGetMetamessagePage(AsyncGetPage):
class AsyncGetDocumentPage(AsyncGetPage):
"""Paginated results for Get Document requests"""
def __init__(self, response: dict, collection, reverse: bool) -> None:
def __init__(
self, response: dict, collection, filter: Optional[dict], reverse: bool
) -> None:
"""Constructor for Page Result from Document Get Request
Args:
@ -218,6 +248,7 @@ class AsyncGetDocumentPage(AsyncGetPage):
"""
super().__init__(response)
self.collection = collection
self.filter = filter
self.reverse = reverse
self.items = [
Document(
@ -232,6 +263,7 @@ class AsyncGetDocumentPage(AsyncGetPage):
async def next(self):
"""Get the next page of results
Returns:
AsyncGetDocumentPage | None: Next Page of Results or None if there
are no more sessions to retreive from a query
@ -239,14 +271,19 @@ class AsyncGetDocumentPage(AsyncGetPage):
if self.page >= self.pages:
return None
return await self.collection.get_documents(
page=self.page + 1, page_size=self.page_size, reverse=self.reverse
filter=self.filter,
page=self.page + 1,
page_size=self.page_size,
reverse=self.reverse,
)
class AsyncGetCollectionPage(AsyncGetPage):
"""Paginated results for Get Collection requests"""
def __init__(self, response: dict, user: AsyncUser, reverse: bool):
def __init__(
self, response: dict, user: AsyncUser, filter: Optional[dict], reverse: bool
):
"""Constructor for page result from Get Collection Request
Args:
@ -256,12 +293,14 @@ class AsyncGetCollectionPage(AsyncGetPage):
"""
super().__init__(response)
self.user = user
self.filter = filter
self.reverse = reverse
self.items = [
AsyncCollection(
user=user,
id=collection["id"],
name=collection["name"],
metadata=collection["metadata"],
created_at=collection["created_at"],
)
for collection in response["items"]
@ -269,6 +308,7 @@ class AsyncGetCollectionPage(AsyncGetPage):
async def next(self):
"""Get the next page of results
Returns:
AsyncGetCollectionPage | None: Next Page of Results or None if
there are no more sessions to retreive from a query
@ -276,6 +316,7 @@ class AsyncGetCollectionPage(AsyncGetPage):
if self.page >= self.pages:
return None
return await self.user.get_collections(
filter=self.filter,
page=self.page + 1,
page_size=self.page_size,
reverse=self.reverse,
@ -390,7 +431,11 @@ class AsyncHoncho:
)
async def get_users(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
):
"""Get Paginated list of users
@ -402,14 +447,24 @@ class AsyncHoncho:
Returns:
AsyncGetUserPage: Paginated list of users
"""
url = f"{self.base_url}/users?page={page}&size={page_size}&reverse={reverse}"
response = await self.client.get(url)
# url = f"{self.base_url}/users?page={page}&size={page_size}&reverse={reverse}"
url = f"{self.base_url}/users"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = await self.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return AsyncGetUserPage(data, self, reverse)
return AsyncGetUserPage(data, self, filter, reverse)
async def get_users_generator(
self,
filter: Optional[dict] = None,
reverse: bool = False,
):
"""Shortcut Generator for get_users. Generator to iterate through
@ -424,7 +479,7 @@ class AsyncHoncho:
"""
page = 1
page_size = 50
get_user_response = await self.get_users(page, page_size, reverse)
get_user_response = await self.get_users(filter, page, page_size, reverse)
while True:
for session in get_user_response.items:
yield session
@ -523,6 +578,7 @@ class AsyncUser:
async def get_sessions(
self,
location_id: Optional[str] = None,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
@ -542,20 +598,33 @@ class AsyncUser:
AsyncGetSessionPage: Page or results for get_sessions query
"""
url = (
f"{self.base_url}/sessions?page={page}&size={page_size}&reverse={reverse}&is_active={is_active}"
+ (f"&location_id={location_id}" if location_id else "")
)
response = await self.honcho.client.get(url)
# url = (
# f"{self.base_url}/sessions?page={page}&size={page_size}&reverse={reverse}&is_active={is_active}"
# + (f"&location_id={location_id}" if location_id else "")
# )
url = f"{self.base_url}/sessions"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
"is_active": is_active,
}
if location_id:
params["location_id"] = location_id
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = await self.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return AsyncGetSessionPage(data, self, reverse, location_id, is_active)
return AsyncGetSessionPage(data, self, reverse, location_id, filter, is_active)
async def get_sessions_generator(
self,
location_id: Optional[str] = None,
reverse: bool = False,
is_active: bool = False,
filter: Optional[dict] = None,
):
"""Shortcut Generator for get_sessions. Generator to iterate through
all sessions for a user in an app
@ -573,7 +642,7 @@ class AsyncUser:
page = 1
page_size = 50
get_session_response = await self.get_sessions(
location_id, page, page_size, reverse, is_active
location_id, filter, page, page_size, reverse, is_active
)
while True:
for session in get_session_response.items:
@ -618,6 +687,7 @@ class AsyncUser:
async def create_collection(
self,
name: str,
metadata: Optional[dict] = None,
):
"""Create a collection for a user
@ -628,7 +698,9 @@ class AsyncUser:
AsyncCollection: The Collection object of the new Collection
"""
data = {"name": name}
if metadata is None:
metadata = {}
data = {"name": name, "metadata": metadata}
url = f"{self.base_url}/collections"
response = await self.honcho.client.post(url, json=data)
response.raise_for_status()
@ -637,6 +709,7 @@ class AsyncUser:
self,
id=data["id"],
name=name,
metadata=metadata,
created_at=data["created_at"],
)
@ -658,11 +731,16 @@ class AsyncUser:
user=self,
id=data["id"],
name=data["name"],
metadata=data["metadata"],
created_at=data["created_at"],
)
async def get_collections(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
):
"""Return collections associated with a user paginated
@ -675,13 +753,24 @@ class AsyncUser:
AsyncGetCollectionPage: Page or results for get_collections query
"""
url = f"{self.base_url}/collections?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
response = await self.honcho.client.get(url)
# url = f"{self.base_url}/collections?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/collections"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = await self.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return AsyncGetCollectionPage(data, self, reverse)
return AsyncGetCollectionPage(data, self, filter, reverse)
async def get_collections_generator(self, reverse: bool = False):
async def get_collections_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_sessions. Generator to iterate through
all sessions for a user in an app
@ -694,7 +783,9 @@ class AsyncUser:
"""
page = 1
page_size = 50
get_collection_response = await self.get_collections(page, page_size, reverse)
get_collection_response = await self.get_collections(
filter, page, page_size, reverse
)
while True:
for collection in get_collection_response.items:
yield collection
@ -740,7 +831,9 @@ class AsyncSession:
"""Returns whether the session is active - made property to prevent tampering"""
return self._is_active
async def create_message(self, is_user: bool, content: str):
async def create_message(
self, is_user: bool, content: str, metadata: Optional[dict] = None
):
"""Adds a message to the session
Args:
@ -753,7 +846,9 @@ class AsyncSession:
"""
if not self.is_active:
raise Exception("Session is inactive")
data = {"is_user": is_user, "content": content}
if metadata is None:
metadata = {}
data = {"is_user": is_user, "content": content, "metadata": metadata}
url = f"{self.base_url}/messages"
response = await self.user.honcho.client.post(url, json=data)
response.raise_for_status()
@ -763,6 +858,7 @@ class AsyncSession:
id=data["id"],
is_user=is_user,
content=content,
metadata=metadata,
created_at=data["created_at"],
)
@ -785,11 +881,16 @@ class AsyncSession:
id=data["id"],
is_user=data["is_user"],
content=data["content"],
metadata=data["metadata"],
created_at=data["created_at"],
)
async def get_messages(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
) -> AsyncGetMessagePage:
"""Get all messages for a session
@ -802,13 +903,23 @@ class AsyncSession:
AsyncGetMessagePage: Page of Message objects
"""
url = f"{self.base_url}/messages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
response = await self.user.honcho.client.get(url)
url = f"{self.base_url}/messages"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = await self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return AsyncGetMessagePage(data, self, reverse)
return AsyncGetMessagePage(data, self, filter, reverse)
async def get_messages_generator(self, reverse: bool = False):
async def get_messages_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_messages. Generator to iterate through
all messages for a session in an app
@ -818,7 +929,7 @@ class AsyncSession:
"""
page = 1
page_size = 50
get_messages_page = await self.get_messages(page, page_size, reverse)
get_messages_page = await self.get_messages(filter, page, page_size, reverse)
while True:
for message in get_messages_page.items:
yield message
@ -830,7 +941,11 @@ class AsyncSession:
get_messages_page = new_messages
async def create_metamessage(
self, message: Message, metamessage_type: str, content: str
self,
message: Message,
metamessage_type: str,
content: str,
metadata: Optional[dict] = None,
):
"""Adds a metamessage to a session and links it to a specific message
@ -845,10 +960,13 @@ class AsyncSession:
"""
if not self.is_active:
raise Exception("Session is inactive")
if metadata is None:
metadata = {}
data = {
"metamessage_type": metamessage_type,
"content": content,
"message_id": message.id,
"metadata": metadata,
}
url = f"{self.base_url}/metamessages"
response = await self.user.honcho.client.post(url, json=data)
@ -859,6 +977,7 @@ class AsyncSession:
message_id=message.id,
metamessage_type=metamessage_type,
content=content,
metadata=metadata,
created_at=data["created_at"],
)
@ -881,6 +1000,7 @@ class AsyncSession:
message_id=data["message_id"],
metamessage_type=data["metamessage_type"],
content=data["content"],
metadata=data["metadata"],
created_at=data["created_at"],
)
@ -888,6 +1008,7 @@ class AsyncSession:
self,
metamessage_type: Optional[str] = None,
message: Optional[Message] = None,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
@ -905,23 +1026,35 @@ class AsyncSession:
list[dict]: List of Message objects
"""
url = f"{self.base_url}/metamessages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
# url = f"{self.base_url}/metamessages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/metamessages"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if metamessage_type:
url += f"&metamessage_type={metamessage_type}"
# url += f"&metamessage_type={metamessage_type}"
params["metamessage_type"] = metamessage_type
if message:
url += f"&message_id={message.id}"
response = await self.user.honcho.client.get(url)
# url += f"&message_id={message.id}"
params["message_id"] = message.id
if filter is not None:
json_metadata = json.dumps(filter)
params["filter"] = json_metadata
response = await self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
message_id = message.id if message else None
return AsyncGetMetamessagePage(
data, self, reverse, message_id, metamessage_type
data, self, filter, reverse, message_id, metamessage_type
)
async def get_metamessages_generator(
self,
metamessage_type: Optional[str] = None,
message: Optional[Message] = None,
filter: Optional[dict] = None,
reverse: bool = False,
):
"""Shortcut Generator for get_metamessages. Generator to iterate
@ -940,6 +1073,7 @@ class AsyncSession:
get_metamessages_page = await self.get_metamessages(
metamessage_type=metamessage_type,
message=message,
filter=filter,
page=page,
page_size=page_size,
reverse=reverse,
@ -970,6 +1104,50 @@ class AsyncSession:
self.metadata = metadata
return success
async def update_message(self, message: Message, metadata: dict):
"""Update the metadata of a message
Args:
message (Message): The message to update
metadata (dict): The new metadata for the message
Returns:
boolean: Whether the message was successfully updated
"""
info = {"metadata": metadata}
url = f"{self.base_url}/messages/{message.id}"
response = await self.user.honcho.client.put(url, json=info)
success = response.status_code < 400
message.metadata = metadata
return success
async def update_metamessage(
self,
metamessage: Metamessage,
metamessage_type: Optional[str],
metadata: Optional[dict],
):
"""Update the metadata of a metamessage
Args:
metamessage (Metamessage): The metamessage to update
metadata (dict): The new metadata for the metamessage
Returns:
boolean: Whether the metamessage was successfully updated
"""
if metadata is None and metamessage_type is None:
raise ValueError("metadata and metamessage_type cannot both be None")
info = {"metamessage_type": metamessage_type, "metadata": metadata}
url = f"{self.base_url}/metamessages/{metamessage.id}"
response = await self.user.honcho.client.put(url, json=info)
success = response.status_code < 400
if metamessage_type is not None:
metamessage.metamessage_type = metamessage_type
if metadata is not None:
metamessage.metadata = metadata
return success
async def close(self):
"""Closes a session by marking it as inactive"""
url = f"{self.base_url}"
@ -977,6 +1155,14 @@ class AsyncSession:
response.raise_for_status()
self._is_active = False
async def chat(self, query) -> str:
url = f"{self.base_url}/chat"
params = {"query": query}
response = await self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return data["content"]
class AsyncCollection:
"""Represents a single collection for a user in an app"""
@ -986,12 +1172,14 @@ class AsyncCollection:
user: AsyncUser,
id: uuid.UUID,
name: str,
metadata: dict,
created_at: datetime.datetime,
):
"""Constructor for Collection"""
self.user = user
self.id: uuid.UUID = id
self.name: str = name
self.metadata: dict = metadata
self.created_at: datetime.datetime = created_at
@property
@ -1003,7 +1191,7 @@ class AsyncCollection:
"""String representation of Collection"""
return f"AsyncCollection(id={self.id}, name={self.name}, created_at={self.created_at})" # noqa: E501
async def update(self, name: str):
async def update(self, name: Optional[str] = None, metadata: Optional[dict] = None):
"""Update the name of the collection
Args:
@ -1012,12 +1200,17 @@ class AsyncCollection:
Returns:
boolean: Whether the session was successfully updated
"""
info = {"name": name}
if metadata is None and name is None:
raise ValueError("metadata and name cannot both be None")
info = {"name": name, "metadata": metadata}
url = f"{self.base_url}"
response = await self.user.honcho.client.put(url, json=info)
response.raise_for_status()
success = response.status_code < 400
self.name = name
if name is not None:
self.name = name
if metadata is not None:
self.metadata = metadata
return success
async def delete(self):
@ -1075,7 +1268,11 @@ class AsyncCollection:
)
async def get_documents(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
) -> AsyncGetDocumentPage:
"""Get all documents for a collection
@ -1087,15 +1284,24 @@ class AsyncCollection:
AsyncGetDocumentPage: Page of Document objects
"""
url = (
f"{self.base_url}/documents?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
)
response = await self.user.honcho.client.get(url)
# url = f"{self.base_url}/documents?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/documents"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = await self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return AsyncGetDocumentPage(data, self, reverse)
return AsyncGetDocumentPage(data, self, filter, reverse)
async def get_documents_generator(self, reverse: bool = False):
async def get_documents_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_documents. Generator to iterate through
all documents for a collection in an app
@ -1105,7 +1311,7 @@ class AsyncCollection:
"""
page = 1
page_size = 50
get_documents_page = await self.get_documents(page, page_size, reverse)
get_documents_page = await self.get_documents(filter, page, page_size, reverse)
while True:
for document in get_documents_page.items:
yield document
@ -1125,8 +1331,10 @@ class AsyncCollection:
Returns:
List[Document]: The response from the query with matching documents
"""
url = f"{self.base_url}/query?query={query}&top_k={top_k}"
response = await self.user.honcho.client.get(url)
# url = f"{self.base_url}/query?query={query}&top_k={top_k}"
url = f"{self.base_url}/query"
params = {"query": query, "top_k": top_k}
response = await self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = [
Document(
@ -1141,7 +1349,10 @@ class AsyncCollection:
return data
async def update_document(
self, document: Document, content: Optional[str], metadata: Optional[dict]
self,
document: Document,
content: Optional[str] = None,
metadata: Optional[dict] = None,
) -> Document:
"""Update a document in the collection

View File

View File

@ -0,0 +1,29 @@
import functools
import importlib
from typing import List
from honcho.schemas import Message
def requires_langchain(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if importlib.util.find_spec("langchain") is None:
raise ImportError("Langchain must be installed to use this feature")
# raise RuntimeError("langchain is not installed")
return func(*args, **kwargs)
return wrapper
@requires_langchain
def langchain_message_converter(messages: List[Message]):
from langchain_core.messages import AIMessage, HumanMessage
new_messages = []
for message in messages:
if message.is_user:
new_messages.append(HumanMessage(content=message.content))
else:
new_messages.append(AIMessage(content=message.content))
return new_messages

View File

@ -1,32 +1,60 @@
import uuid
import datetime
class Message:
def __init__(self, session_id: uuid.UUID, id: uuid.UUID, is_user: bool, content: str, created_at: datetime.datetime):
def __init__(
self,
session_id: uuid.UUID,
id: uuid.UUID,
is_user: bool,
content: str,
metadata: dict,
created_at: datetime.datetime,
):
"""Constructor for Message"""
self.session_id = session_id
self.id = id
self.is_user = is_user
self.content = content
self.metadata = metadata
self.created_at = created_at
def __str__(self):
return f"Message(id={self.id}, is_user={self.is_user}, content={self.content})"
class Metamessage:
def __init__(self, id: uuid.UUID, message_id: uuid.UUID, metamessage_type: str, content: str, created_at: datetime.datetime):
def __init__(
self,
id: uuid.UUID,
message_id: uuid.UUID,
metamessage_type: str,
content: str,
metadata: dict,
created_at: datetime.datetime,
):
"""Constructor for Metamessage"""
self.id = id
self.message_id = message_id
self.metamessage_type = metamessage_type
self.content = content
self.metadata = metadata
self.created_at = created_at
def __str__(self):
return f"Metamessage(id={self.id}, message_id={self.message_id}, metamessage_type={self.metamessage_type}, content={self.content})"
class Document:
def __init__(self, id: uuid.UUID, collection_id: uuid.UUID, content: str, metadata: dict, created_at: datetime.datetime):
def __init__(
self,
id: uuid.UUID,
collection_id: uuid.UUID,
content: str,
metadata: dict,
created_at: datetime.datetime,
):
"""Constructor for Document"""
self.collection_id = collection_id
self.id = id
@ -36,4 +64,3 @@ class Document:
def __str__(self) -> str:
return f"Document(id={self.id}, metadata={self.metadata}, content={self.content}, created_at={self.created_at})"

View File

@ -1,6 +1,7 @@
from __future__ import annotations
import datetime
import json
import uuid
from typing import Optional
@ -32,7 +33,13 @@ class GetPage:
class GetUserPage(GetPage):
"""Paginated Results for Get User Requests"""
def __init__(self, response: dict, honcho: Honcho, reverse: bool):
def __init__(
self,
response: dict,
honcho: Honcho,
filter: Optional[dict],
reverse: bool,
):
"""Constructor for Page Result from User Get Request
Args:
@ -42,6 +49,7 @@ class GetUserPage(GetPage):
"""
super().__init__(response)
self.honcho = honcho
self.filter = filter
self.reverse = reverse
self.items = [
User(
@ -57,7 +65,10 @@ class GetUserPage(GetPage):
if self.page >= self.pages:
return None
return self.honcho.get_users(
page=(self.page + 1), page_size=self.page_size, reverse=self.reverse
filter=self.filter,
page=(self.page + 1),
page_size=self.page_size,
reverse=self.reverse,
)
@ -70,6 +81,7 @@ class GetSessionPage(GetPage):
user: User,
reverse: bool,
location_id: Optional[str],
filter: Optional[dict],
is_active: bool,
):
"""Constructor for Page Result from Session Get Request
@ -85,6 +97,7 @@ class GetSessionPage(GetPage):
self.location_id = location_id
self.reverse = reverse
self.is_active = is_active
self.filter = filter
self.items = [
Session(
user=user,
@ -99,14 +112,15 @@ class GetSessionPage(GetPage):
def next(self):
"""Get the next page of results
Returns:
GetSessionPage | None: Next Page of Results or None if there
are no more sessions to retreive from a query
GetSessionPage | None: Next Page of Results or None if there are no more sessions to retreive from a query
"""
if self.page >= self.pages:
return None
return self.user.get_sessions(
location_id=self.location_id,
filter=self.filter,
page=(self.page + 1),
page_size=self.page_size,
reverse=self.reverse,
@ -117,7 +131,13 @@ class GetSessionPage(GetPage):
class GetMessagePage(GetPage):
"""Paginated Results for Get Session Requests"""
def __init__(self, response: dict, session: Session, reverse: bool):
def __init__(
self,
response: dict,
session: Session,
filter: Optional[dict],
reverse: bool,
):
"""Constructor for Page Result from Session Get Request
Args:
@ -127,6 +147,7 @@ class GetMessagePage(GetPage):
"""
super().__init__(response)
self.session = session
self.filter = filter
self.reverse = reverse
self.items = [
Message(
@ -134,6 +155,7 @@ class GetMessagePage(GetPage):
id=message["id"],
is_user=message["is_user"],
content=message["content"],
metadata=message["metadata"],
created_at=message["created_at"],
)
for message in response["items"]
@ -141,6 +163,7 @@ class GetMessagePage(GetPage):
def next(self):
"""Get the next page of results
Returns:
GetMessagePage | None: Next Page of Results or None if there
are no more messages to retreive from a query
@ -148,7 +171,7 @@ class GetMessagePage(GetPage):
if self.page >= self.pages:
return None
return self.session.get_messages(
(self.page + 1), self.page_size, self.reverse
self.filter, (self.page + 1), self.page_size, self.reverse
)
@ -157,6 +180,7 @@ class GetMetamessagePage(GetPage):
self,
response: dict,
session,
filter: Optional[dict],
reverse: bool,
message_id: Optional[uuid.UUID],
metamessage_type: Optional[str],
@ -175,11 +199,13 @@ class GetMetamessagePage(GetPage):
self.session = session
self.message_id = message_id
self.metamessage_type = metamessage_type
self.filter = filter
self.reverse = reverse
self.items = [
Metamessage(
id=metamessage["id"],
message_id=metamessage["message_id"],
metadata=metamessage["metadata"],
metamessage_type=metamessage["metamessage_type"],
content=metamessage["content"],
created_at=metamessage["created_at"],
@ -189,6 +215,7 @@ class GetMetamessagePage(GetPage):
def next(self):
"""Get the next page of results
Returns:
GetMetamessagePage | None: Next Page of Results or None if
there are no more metamessages to retreive from a query
@ -197,6 +224,7 @@ class GetMetamessagePage(GetPage):
return None
return self.session.get_metamessages(
metamessage_type=self.metamessage_type,
filter=self.filter,
message=self.message_id,
page=(self.page + 1),
page_size=self.page_size,
@ -207,7 +235,9 @@ class GetMetamessagePage(GetPage):
class GetDocumentPage(GetPage):
"""Paginated results for Get Document requests"""
def __init__(self, response: dict, collection, reverse: bool) -> None:
def __init__(
self, response: dict, collection, filter: Optional[dict], reverse: bool
) -> None:
"""Constructor for Page Result from Document Get Request
Args:
@ -218,6 +248,7 @@ class GetDocumentPage(GetPage):
"""
super().__init__(response)
self.collection = collection
self.filter = filter
self.reverse = reverse
self.items = [
Document(
@ -232,6 +263,7 @@ class GetDocumentPage(GetPage):
def next(self):
"""Get the next page of results
Returns:
GetDocumentPage | None: Next Page of Results or None if there
are no more sessions to retreive from a query
@ -239,14 +271,19 @@ class GetDocumentPage(GetPage):
if self.page >= self.pages:
return None
return self.collection.get_documents(
page=self.page + 1, page_size=self.page_size, reverse=self.reverse
filter=self.filter,
page=self.page + 1,
page_size=self.page_size,
reverse=self.reverse,
)
class GetCollectionPage(GetPage):
"""Paginated results for Get Collection requests"""
def __init__(self, response: dict, user: User, reverse: bool):
def __init__(
self, response: dict, user: User, filter: Optional[dict], reverse: bool
):
"""Constructor for page result from Get Collection Request
Args:
@ -256,12 +293,14 @@ class GetCollectionPage(GetPage):
"""
super().__init__(response)
self.user = user
self.filter = filter
self.reverse = reverse
self.items = [
Collection(
user=user,
id=collection["id"],
name=collection["name"],
metadata=collection["metadata"],
created_at=collection["created_at"],
)
for collection in response["items"]
@ -269,6 +308,7 @@ class GetCollectionPage(GetPage):
def next(self):
"""Get the next page of results
Returns:
GetCollectionPage | None: Next Page of Results or None if
there are no more sessions to retreive from a query
@ -276,6 +316,7 @@ class GetCollectionPage(GetPage):
if self.page >= self.pages:
return None
return self.user.get_collections(
filter=self.filter,
page=self.page + 1,
page_size=self.page_size,
reverse=self.reverse,
@ -390,7 +431,11 @@ class Honcho:
)
def get_users(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
):
"""Get Paginated list of users
@ -402,14 +447,24 @@ class Honcho:
Returns:
GetUserPage: Paginated list of users
"""
url = f"{self.base_url}/users?page={page}&size={page_size}&reverse={reverse}"
response = self.client.get(url)
# url = f"{self.base_url}/users?page={page}&size={page_size}&reverse={reverse}"
url = f"{self.base_url}/users"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = self.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return GetUserPage(data, self, reverse)
return GetUserPage(data, self, filter, reverse)
def get_users_generator(
self,
filter: Optional[dict] = None,
reverse: bool = False,
):
"""Shortcut Generator for get_users. Generator to iterate through
@ -424,7 +479,7 @@ class Honcho:
"""
page = 1
page_size = 50
get_user_response = self.get_users(page, page_size, reverse)
get_user_response = self.get_users(filter, page, page_size, reverse)
while True:
for session in get_user_response.items:
yield session
@ -523,6 +578,7 @@ class User:
def get_sessions(
self,
location_id: Optional[str] = None,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
@ -542,20 +598,33 @@ class User:
GetSessionPage: Page or results for get_sessions query
"""
url = (
f"{self.base_url}/sessions?page={page}&size={page_size}&reverse={reverse}&is_active={is_active}"
+ (f"&location_id={location_id}" if location_id else "")
)
response = self.honcho.client.get(url)
# url = (
# f"{self.base_url}/sessions?page={page}&size={page_size}&reverse={reverse}&is_active={is_active}"
# + (f"&location_id={location_id}" if location_id else "")
# )
url = f"{self.base_url}/sessions"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
"is_active": is_active,
}
if location_id:
params["location_id"] = location_id
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = self.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return GetSessionPage(data, self, reverse, location_id, is_active)
return GetSessionPage(data, self, reverse, location_id, filter, is_active)
def get_sessions_generator(
self,
location_id: Optional[str] = None,
reverse: bool = False,
is_active: bool = False,
filter: Optional[dict] = None,
):
"""Shortcut Generator for get_sessions. Generator to iterate through
all sessions for a user in an app
@ -573,7 +642,7 @@ class User:
page = 1
page_size = 50
get_session_response = self.get_sessions(
location_id, page, page_size, reverse, is_active
location_id, filter, page, page_size, reverse, is_active
)
while True:
for session in get_session_response.items:
@ -618,6 +687,7 @@ class User:
def create_collection(
self,
name: str,
metadata: Optional[dict] = None,
):
"""Create a collection for a user
@ -628,7 +698,9 @@ class User:
Collection: The Collection object of the new Collection
"""
data = {"name": name}
if metadata is None:
metadata = {}
data = {"name": name, "metadata": metadata}
url = f"{self.base_url}/collections"
response = self.honcho.client.post(url, json=data)
response.raise_for_status()
@ -637,6 +709,7 @@ class User:
self,
id=data["id"],
name=name,
metadata=metadata,
created_at=data["created_at"],
)
@ -658,11 +731,16 @@ class User:
user=self,
id=data["id"],
name=data["name"],
metadata=data["metadata"],
created_at=data["created_at"],
)
def get_collections(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
):
"""Return collections associated with a user paginated
@ -675,13 +753,24 @@ class User:
GetCollectionPage: Page or results for get_collections query
"""
url = f"{self.base_url}/collections?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
response = self.honcho.client.get(url)
# url = f"{self.base_url}/collections?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/collections"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = self.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return GetCollectionPage(data, self, reverse)
return GetCollectionPage(data, self, filter, reverse)
def get_collections_generator(self, reverse: bool = False):
def get_collections_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_sessions. Generator to iterate through
all sessions for a user in an app
@ -694,7 +783,9 @@ class User:
"""
page = 1
page_size = 50
get_collection_response = self.get_collections(page, page_size, reverse)
get_collection_response = self.get_collections(
filter, page, page_size, reverse
)
while True:
for collection in get_collection_response.items:
yield collection
@ -740,7 +831,9 @@ class Session:
"""Returns whether the session is active - made property to prevent tampering"""
return self._is_active
def create_message(self, is_user: bool, content: str):
def create_message(
self, is_user: bool, content: str, metadata: Optional[dict] = None
):
"""Adds a message to the session
Args:
@ -753,7 +846,9 @@ class Session:
"""
if not self.is_active:
raise Exception("Session is inactive")
data = {"is_user": is_user, "content": content}
if metadata is None:
metadata = {}
data = {"is_user": is_user, "content": content, "metadata": metadata}
url = f"{self.base_url}/messages"
response = self.user.honcho.client.post(url, json=data)
response.raise_for_status()
@ -763,6 +858,7 @@ class Session:
id=data["id"],
is_user=is_user,
content=content,
metadata=metadata,
created_at=data["created_at"],
)
@ -785,11 +881,16 @@ class Session:
id=data["id"],
is_user=data["is_user"],
content=data["content"],
metadata=data["metadata"],
created_at=data["created_at"],
)
def get_messages(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
) -> GetMessagePage:
"""Get all messages for a session
@ -802,13 +903,23 @@ class Session:
GetMessagePage: Page of Message objects
"""
url = f"{self.base_url}/messages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
response = self.user.honcho.client.get(url)
url = f"{self.base_url}/messages"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return GetMessagePage(data, self, reverse)
return GetMessagePage(data, self, filter, reverse)
def get_messages_generator(self, reverse: bool = False):
def get_messages_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_messages. Generator to iterate through
all messages for a session in an app
@ -818,7 +929,7 @@ class Session:
"""
page = 1
page_size = 50
get_messages_page = self.get_messages(page, page_size, reverse)
get_messages_page = self.get_messages(filter, page, page_size, reverse)
while True:
for message in get_messages_page.items:
yield message
@ -830,7 +941,11 @@ class Session:
get_messages_page = new_messages
def create_metamessage(
self, message: Message, metamessage_type: str, content: str
self,
message: Message,
metamessage_type: str,
content: str,
metadata: Optional[dict] = None,
):
"""Adds a metamessage to a session and links it to a specific message
@ -845,10 +960,13 @@ class Session:
"""
if not self.is_active:
raise Exception("Session is inactive")
if metadata is None:
metadata = {}
data = {
"metamessage_type": metamessage_type,
"content": content,
"message_id": message.id,
"metadata": metadata,
}
url = f"{self.base_url}/metamessages"
response = self.user.honcho.client.post(url, json=data)
@ -859,6 +977,7 @@ class Session:
message_id=message.id,
metamessage_type=metamessage_type,
content=content,
metadata=metadata,
created_at=data["created_at"],
)
@ -881,6 +1000,7 @@ class Session:
message_id=data["message_id"],
metamessage_type=data["metamessage_type"],
content=data["content"],
metadata=data["metadata"],
created_at=data["created_at"],
)
@ -888,6 +1008,7 @@ class Session:
self,
metamessage_type: Optional[str] = None,
message: Optional[Message] = None,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
@ -905,23 +1026,35 @@ class Session:
list[dict]: List of Message objects
"""
url = f"{self.base_url}/metamessages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
# url = f"{self.base_url}/metamessages?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/metamessages"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if metamessage_type:
url += f"&metamessage_type={metamessage_type}"
# url += f"&metamessage_type={metamessage_type}"
params["metamessage_type"] = metamessage_type
if message:
url += f"&message_id={message.id}"
response = self.user.honcho.client.get(url)
# url += f"&message_id={message.id}"
params["message_id"] = message.id
if filter is not None:
json_metadata = json.dumps(filter)
params["filter"] = json_metadata
response = self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
message_id = message.id if message else None
return GetMetamessagePage(
data, self, reverse, message_id, metamessage_type
data, self, filter, reverse, message_id, metamessage_type
)
def get_metamessages_generator(
self,
metamessage_type: Optional[str] = None,
message: Optional[Message] = None,
filter: Optional[dict] = None,
reverse: bool = False,
):
"""Shortcut Generator for get_metamessages. Generator to iterate
@ -940,6 +1073,7 @@ class Session:
get_metamessages_page = self.get_metamessages(
metamessage_type=metamessage_type,
message=message,
filter=filter,
page=page,
page_size=page_size,
reverse=reverse,
@ -970,6 +1104,50 @@ class Session:
self.metadata = metadata
return success
def update_message(self, message: Message, metadata: dict):
"""Update the metadata of a message
Args:
message (Message): The message to update
metadata (dict): The new metadata for the message
Returns:
boolean: Whether the message was successfully updated
"""
info = {"metadata": metadata}
url = f"{self.base_url}/messages/{message.id}"
response = self.user.honcho.client.put(url, json=info)
success = response.status_code < 400
message.metadata = metadata
return success
def update_metamessage(
self,
metamessage: Metamessage,
metamessage_type: Optional[str],
metadata: Optional[dict],
):
"""Update the metadata of a metamessage
Args:
metamessage (Metamessage): The metamessage to update
metadata (dict): The new metadata for the metamessage
Returns:
boolean: Whether the metamessage was successfully updated
"""
if metadata is None and metamessage_type is None:
raise ValueError("metadata and metamessage_type cannot both be None")
info = {"metamessage_type": metamessage_type, "metadata": metadata}
url = f"{self.base_url}/metamessages/{metamessage.id}"
response = self.user.honcho.client.put(url, json=info)
success = response.status_code < 400
if metamessage_type is not None:
metamessage.metamessage_type = metamessage_type
if metadata is not None:
metamessage.metadata = metadata
return success
def close(self):
"""Closes a session by marking it as inactive"""
url = f"{self.base_url}"
@ -977,6 +1155,14 @@ class Session:
response.raise_for_status()
self._is_active = False
def chat(self, query) -> str:
url = f"{self.base_url}/chat"
params = {"query": query}
response = self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return data["content"]
class Collection:
"""Represents a single collection for a user in an app"""
@ -986,12 +1172,14 @@ class Collection:
user: User,
id: uuid.UUID,
name: str,
metadata: dict,
created_at: datetime.datetime,
):
"""Constructor for Collection"""
self.user = user
self.id: uuid.UUID = id
self.name: str = name
self.metadata: dict = metadata
self.created_at: datetime.datetime = created_at
@property
@ -1003,7 +1191,7 @@ class Collection:
"""String representation of Collection"""
return f"Collection(id={self.id}, name={self.name}, created_at={self.created_at})" # noqa: E501
def update(self, name: str):
def update(self, name: Optional[str] = None, metadata: Optional[dict] = None):
"""Update the name of the collection
Args:
@ -1012,12 +1200,17 @@ class Collection:
Returns:
boolean: Whether the session was successfully updated
"""
info = {"name": name}
if metadata is None and name is None:
raise ValueError("metadata and name cannot both be None")
info = {"name": name, "metadata": metadata}
url = f"{self.base_url}"
response = self.user.honcho.client.put(url, json=info)
response.raise_for_status()
success = response.status_code < 400
self.name = name
if name is not None:
self.name = name
if metadata is not None:
self.metadata = metadata
return success
def delete(self):
@ -1075,7 +1268,11 @@ class Collection:
)
def get_documents(
self, page: int = 1, page_size: int = 50, reverse: bool = False
self,
filter: Optional[dict] = None,
page: int = 1,
page_size: int = 50,
reverse: bool = False,
) -> GetDocumentPage:
"""Get all documents for a collection
@ -1087,15 +1284,24 @@ class Collection:
GetDocumentPage: Page of Document objects
"""
url = (
f"{self.base_url}/documents?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
)
response = self.user.honcho.client.get(url)
# url = f"{self.base_url}/documents?page={page}&size={page_size}&reverse={reverse}" # noqa: E501
url = f"{self.base_url}/documents"
params = {
"page": page,
"size": page_size,
"reverse": reverse,
}
if filter is not None:
json_filter = json.dumps(filter)
params["filter"] = json_filter
response = self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = response.json()
return GetDocumentPage(data, self, reverse)
return GetDocumentPage(data, self, filter, reverse)
def get_documents_generator(self, reverse: bool = False):
def get_documents_generator(
self, filter: Optional[dict] = None, reverse: bool = False
):
"""Shortcut Generator for get_documents. Generator to iterate through
all documents for a collection in an app
@ -1105,7 +1311,7 @@ class Collection:
"""
page = 1
page_size = 50
get_documents_page = self.get_documents(page, page_size, reverse)
get_documents_page = self.get_documents(filter, page, page_size, reverse)
while True:
for document in get_documents_page.items:
yield document
@ -1125,8 +1331,10 @@ class Collection:
Returns:
List[Document]: The response from the query with matching documents
"""
url = f"{self.base_url}/query?query={query}&top_k={top_k}"
response = self.user.honcho.client.get(url)
# url = f"{self.base_url}/query?query={query}&top_k={top_k}"
url = f"{self.base_url}/query"
params = {"query": query, "top_k": top_k}
response = self.user.honcho.client.get(url, params=params)
response.raise_for_status()
data = [
Document(
@ -1141,7 +1349,10 @@ class Collection:
return data
def update_document(
self, document: Document, content: Optional[str], metadata: Optional[dict]
self,
document: Document,
content: Optional[str] = None,
metadata: Optional[dict] = None,
) -> Document:
"""Update a document in the collection

670
sdk/poetry.lock generated
View File

@ -1,15 +1,27 @@
# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand.
[[package]]
name = "alabaster"
version = "0.7.16"
description = "A light, configurable Sphinx theme"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"},
{file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"},
]
[[package]]
name = "anyio"
version = "4.2.0"
version = "4.3.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
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]
@ -23,16 +35,153 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
trio = ["trio (>=0.23)"]
[[package]]
name = "babel"
version = "2.14.0"
description = "Internationalization utilities"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
{file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
]
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
[[package]]
name = "beautifulsoup4"
version = "4.12.3"
description = "Screen-scraping library"
category = "dev"
optional = false
python-versions = ">=3.6.0"
files = [
{file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
{file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
]
[package.dependencies]
soupsieve = ">1.2"
[package.extras]
cchardet = ["cchardet"]
chardet = ["chardet"]
charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "certifi"
version = "2023.11.17"
version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
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]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "dev"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
@ -47,6 +196,83 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.4.3"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
]
[package.extras]
toml = ["tomli"]
[[package]]
name = "docutils"
version = "0.20.1"
description = "Docutils -- Python Documentation Utilities"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"},
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.0"
@ -62,6 +288,24 @@ files = [
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "furo"
version = "2024.1.29"
description = "A clean customisable Sphinx documentation theme."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"},
{file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"},
]
[package.dependencies]
beautifulsoup4 = "*"
pygments = ">=2.7"
sphinx = ">=6.0,<8.0"
sphinx-basic-ng = "*"
[[package]]
name = "h11"
version = "0.14.0"
@ -76,14 +320,14 @@ files = [
[[package]]
name = "httpcore"
version = "1.0.2"
version = "1.0.4"
description = "A minimal low-level HTTP client."
category = "main"
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]
@ -94,7 +338,7 @@ h11 = ">=0.13,<0.15"
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
trio = ["trio (>=0.22.0,<0.23.0)"]
trio = ["trio (>=0.22.0,<0.25.0)"]
[[package]]
name = "httpx"
@ -133,6 +377,38 @@ files = [
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
]
[[package]]
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
{file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
]
[[package]]
name = "importlib-metadata"
version = "7.0.2"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"},
{file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "iniconfig"
version = "2.0.0"
@ -145,34 +421,138 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "jinja2"
version = "3.1.3"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
{file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "markupsafe"
version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{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"
category = "dev"
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 = "pluggy"
version = "1.3.0"
version = "1.4.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
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.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pygments"
version = "2.17.2"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
{file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
]
[package.extras]
plugins = ["importlib-metadata"]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
version = "7.4.4"
@ -198,35 +578,235 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
[[package]]
name = "pytest-asyncio"
version = "0.23.4"
version = "0.23.5.post1"
description = "Pytest support for asyncio"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-asyncio-0.23.4.tar.gz", hash = "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2"},
{file = "pytest_asyncio-0.23.4-py3-none-any.whl", hash = "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef"},
{file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"},
{file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"},
]
[package.dependencies]
pytest = ">=7.0.0,<8"
pytest = ">=7.0.0,<9"
[package.extras]
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "sniffio"
version = "1.3.0"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
category = "main"
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]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
]
[[package]]
name = "soupsieve"
version = "2.5"
description = "A modern CSS selector implementation for Beautiful Soup."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
[[package]]
name = "sphinx"
version = "7.2.6"
description = "Python documentation generator"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"},
{file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"},
]
[package.dependencies]
alabaster = ">=0.7,<0.8"
babel = ">=2.9"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.18.1,<0.21"
imagesize = ">=1.3"
importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""}
Jinja2 = ">=3.0"
packaging = ">=21.0"
Pygments = ">=2.14"
requests = ">=2.25.0"
snowballstemmer = ">=2.0"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = ">=1.1.9"
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"]
test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"]
[[package]]
name = "sphinx-basic-ng"
version = "1.0.0b2"
description = "A modern skeleton for Sphinx themes."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"},
{file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"},
]
[package.dependencies]
sphinx = ">=4.0"
[package.extras]
docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"]
[[package]]
name = "sphinxcontrib-applehelp"
version = "1.0.8"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"},
{file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
version = "1.0.6"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"},
{file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.0.5"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"},
{file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
standalone = ["Sphinx (>=5)"]
test = ["html5lib", "pytest"]
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
[package.extras]
test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-qthelp"
version = "1.0.7"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"},
{file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "1.1.10"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
{file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"},
{file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "tomli"
version = "2.0.1"
@ -241,17 +821,51 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.9.0"
version = "4.10.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
category = "main"
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]]
name = "urllib3"
version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "zipp"
version = "3.18.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.18.0-py3-none-any.whl", hash = "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79"},
{file = "zipp-3.18.0.tar.gz", hash = "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "6ccea662fa5a5bae88618123d5d05e0d4955c234b7e1a688d2fae2f90cd9f7f8"
python-versions = "^3.9"
content-hash = "bf46c7a976758553f5f41e0d73766388e5b93ccf383d7870106286c5c570cedf"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "honcho-ai"
version = "0.0.4"
version = "0.0.5"
description = "Python Client SDK for Honcho"
authors = ["Plastic Labs <hello@plasticlabs.ai>"]
license = "AGPL-3.0"
@ -8,12 +8,17 @@ readme = "README.md"
packages = [{include = "honcho"}]
[tool.poetry.dependencies]
python = "^3.10"
python = "^3.9"
httpx = "^0.26.0"
[tool.poetry.group.test.dependencies]
pytest = "^7.4.4"
pytest-asyncio = "^0.23.4"
coverage = "^7.4.3"
[tool.poetry.group.docs.dependencies]
sphinx = "^7.2.6"
furo = "^2024.1.29"
[tool.ruff.lint]
# from https://docs.astral.sh/ruff/linter/#rule-selection example

View File

@ -15,6 +15,45 @@ from honcho import (
from honcho import AsyncHoncho as Honcho
@pytest.mark.asyncio
async def test_session_metadata_filter():
app_name = str(uuid1())
user_name = str(uuid1())
honcho = Honcho(app_name, "http://localhost:8000")
await honcho.initialize()
user = await honcho.create_user(user_name)
await user.create_session()
await user.create_session(metadata={"foo": "bar"})
await user.create_session(metadata={"foo": "bar"})
response = await user.get_sessions(filter={"foo": "bar"})
retrieved_sessions = response.items
assert len(retrieved_sessions) == 2
response = await user.get_sessions()
assert len(response.items) == 3
@pytest.mark.asyncio
async def test_delete_session_metadata():
app_name = str(uuid1())
user_name = str(uuid1())
honcho = Honcho(app_name, "http://localhost:8000")
await honcho.initialize()
user = await honcho.create_user(user_name)
retrieved_session = await user.create_session(metadata={"foo": "bar"})
assert retrieved_session.metadata == {"foo": "bar"}
await retrieved_session.update(metadata={})
session_copy = await user.get_session(retrieved_session.id)
assert session_copy.metadata == {}
@pytest.mark.asyncio
async def test_user_update():
user_name = str(uuid1())

View File

@ -15,6 +15,43 @@ from honcho import (
from honcho import Honcho as Honcho
def test_session_metadata_filter():
app_name = str(uuid1())
user_name = str(uuid1())
honcho = Honcho(app_name, "http://localhost:8000")
honcho.initialize()
user = honcho.create_user(user_name)
user.create_session()
user.create_session(metadata={"foo": "bar"})
user.create_session(metadata={"foo": "bar"})
response = user.get_sessions(filter={"foo": "bar"})
retrieved_sessions = response.items
assert len(retrieved_sessions) == 2
response = user.get_sessions()
assert len(response.items) == 3
def test_delete_session_metadata():
app_name = str(uuid1())
user_name = str(uuid1())
honcho = Honcho(app_name, "http://localhost:8000")
honcho.initialize()
user = honcho.create_user(user_name)
retrieved_session = user.create_session(metadata={"foo": "bar"})
assert retrieved_session.metadata == {"foo": "bar"}
retrieved_session.update(metadata={})
session_copy = user.get_session(retrieved_session.id)
assert session_copy.metadata == {}
def test_user_update():
user_name = str(uuid1())
app_name = str(uuid1())