├── src ├── __init__.py ├── configs.py ├── schemas.py ├── examples │ ├── simple_example.py │ ├── email_agent.py │ └── triage_agent.py ├── utils.py └── core.py ├── slides └── LLM-Agents.pdf ├── pyproject.toml ├── README.md ├── .gitignore └── poetry.lock /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /slides/LLM-Agents.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mamdouh66/LLM-Agents-Tutorial/HEAD/slides/LLM-Agents.pdf -------------------------------------------------------------------------------- /src/configs.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings, SettingsConfigDict 2 | 3 | 4 | class Settings(BaseSettings): 5 | model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") 6 | 7 | OPENAI_API_KEY: str 8 | SMALL_OPENAI_MODEL_NAME: str = "gpt-4o-mini" 9 | BIG_OPENAI_MODEL_NAME: str = "gpt-4o" 10 | 11 | 12 | settings = Settings() 13 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "src" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Mamdouh"] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.11" 10 | openai = "^1.60.1" 11 | pydantic = "^2.10.6" 12 | pydantic-settings = "^2.7.1" 13 | rich = "^13.9.4" 14 | 15 | 16 | [build-system] 17 | requires = ["poetry-core"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /src/schemas.py: -------------------------------------------------------------------------------- 1 | from src.configs import settings 2 | 3 | from openai.types.chat import ChatCompletionMessage 4 | from openai.types.chat.chat_completion_message_tool_call import ( 5 | ChatCompletionMessageToolCall, 6 | Function, 7 | ) 8 | from typing import List, Callable, Union, Optional 9 | 10 | from pydantic import BaseModel 11 | 12 | AgentFunction = Callable[[], Union[str, "Agent", dict]] 13 | 14 | 15 | class Agent(BaseModel): 16 | name: str = "Agent" 17 | model: str = settings.BIG_OPENAI_MODEL_NAME 18 | instructions: Union[str, Callable[[], str]] = "You are a helpful agent." 19 | functions: List[AgentFunction] = [] 20 | 21 | 22 | class Response(BaseModel): 23 | messages: List = [] 24 | agent: Optional[Agent] = None 25 | 26 | 27 | class Result(BaseModel): 28 | """ 29 | Encapsulates the possible return values for an agent function. 30 | 31 | Attributes: 32 | value (str): The result value as a string. 33 | agent (Agent): The agent instance, if applicable. 34 | """ 35 | 36 | value: str = "" 37 | agent: Optional[Agent] = None 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLM Agents from scratch 2 | 3 | This is a tutorial to explain LLM systems and agents from scratch and why we don't need any frameworks. 4 | 5 | ## Resources Used 6 | 7 | This tutorial was heavly dependant on a couple of resources: 8 | 9 | - [Anthropic's blog](https://www.anthropic.com/research/building-effective-agents) 10 | - [Google's whitepaper](https://www.kaggle.com/whitepaper-agents) 11 | - [OpenAI's Swarm](https://cookbook.openai.com/examples/orchestrating_agents) 12 | 13 | Good things to read: 14 | 15 | - [Octomind's experinece with blog](https://www.octomind.dev/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents) 16 | - [OpenAI's function calling](https://platform.openai.com/docs/guides/function-calling?example=send-email&strict-mode-examples=strict-enabled) 17 | - [Agents Recipes](https://www.agentrecipes.com/) 18 | - [Building A Generative AI Platform](https://huyenchip.com/2024/07/25/genai-platform.html) 19 | - [OpenAI's Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs?lang=python) 20 | - [Prompt Engineering Guide](https://www.promptingguide.ai/) 21 | -------------------------------------------------------------------------------- /src/examples/simple_example.py: -------------------------------------------------------------------------------- 1 | from src.core import Orchestrator, Agent 2 | 3 | client = Orchestrator() 4 | 5 | 6 | english_agent = Agent( 7 | name="English Agent", 8 | instructions="You are an agent that speaks in English", 9 | ) 10 | 11 | arabic_agent = Agent( 12 | name="Arabic Agent", 13 | instructions="You are an agent that speaks in Arabic", 14 | ) 15 | 16 | 17 | def transfer_to_arabic_agent(): 18 | """Transfer arabic speaking users immediately.""" 19 | return arabic_agent 20 | 21 | 22 | def transfer_to_english_agent(): 23 | """Transfer english speaking users immediately.""" 24 | return english_agent 25 | 26 | 27 | english_agent.functions.append(transfer_to_arabic_agent) 28 | arabic_agent.functions.append(transfer_to_english_agent) 29 | 30 | 31 | def run_demo_loop(starting_agent, debug=False) -> None: 32 | client = Orchestrator() 33 | print("Starting Simple Example CLI") 34 | 35 | messages = [] 36 | agent = starting_agent 37 | 38 | while True: 39 | user_input = input("\033[90mUser\033[0m: (Enter 'q' to quit) ") 40 | if user_input.lower() == "q": 41 | break 42 | 43 | messages.append({"role": "user", "content": user_input}) 44 | 45 | response = client.run( 46 | agent=agent, 47 | messages=messages, 48 | debug=debug, 49 | ) 50 | 51 | print(f'{response.agent.name}: {response.messages[-1]["content"]}') 52 | messages.extend(response.messages) 53 | agent = response.agent 54 | 55 | 56 | if __name__ == "__main__": 57 | run_demo_loop(english_agent, debug=True) 58 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from datetime import datetime 3 | 4 | 5 | def debug_print(debug: bool, *args: str) -> None: 6 | if not debug: 7 | return 8 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 9 | message = " ".join(map(str, args)) 10 | print(f"\033[97m[\033[90m{timestamp}\033[97m]\033[90m {message}\033[0m") 11 | 12 | 13 | def function_to_json(func) -> dict: 14 | type_map = { 15 | str: "string", 16 | int: "integer", 17 | float: "number", 18 | bool: "boolean", 19 | list: "array", 20 | dict: "object", 21 | type(None): "null", 22 | } 23 | 24 | try: 25 | signature = inspect.signature(func) 26 | except ValueError as e: 27 | raise ValueError( 28 | f"Failed to get signature for function {func.__name__}: {str(e)}" 29 | ) 30 | 31 | parameters = {} 32 | for param in signature.parameters.values(): 33 | try: 34 | param_type = type_map.get(param.annotation, "string") 35 | except KeyError as e: 36 | raise KeyError( 37 | f"Unknown type annotation {param.annotation} for parameter {param.name}: {str(e)}" 38 | ) 39 | parameters[param.name] = {"type": param_type} 40 | 41 | required = [ 42 | param.name 43 | for param in signature.parameters.values() 44 | if param.default == inspect._empty 45 | ] 46 | 47 | return { 48 | "type": "function", 49 | "function": { 50 | "name": func.__name__, 51 | "description": func.__doc__ or "", 52 | "parameters": { 53 | "type": "object", 54 | "properties": parameters, 55 | "required": required, 56 | }, 57 | }, 58 | } 59 | -------------------------------------------------------------------------------- /src/examples/email_agent.py: -------------------------------------------------------------------------------- 1 | from src.core import Orchestrator, Agent 2 | 3 | client = Orchestrator() 4 | 5 | 6 | def send_email(recipient, subject, body): 7 | """Send an email to a recipient with the given subject and body.""" 8 | print("Sending email...") 9 | print(f"To: {recipient}") 10 | print(f"Subject: {subject}") 11 | print(f"Body: {body}") 12 | return "Email sent successfully!" 13 | 14 | 15 | def schedule_email(recipient, subject, body, send_time): 16 | """Schedule an email to be sent at a specific time.""" 17 | print(f"Scheduling email for {send_time}...") 18 | print(f"To: {recipient}") 19 | print(f"Subject: {subject}") 20 | print(f"Body: {body}") 21 | return f"Email scheduled to be sent at {send_time}" 22 | 23 | 24 | email_agent = Agent( 25 | name="Email Assistant", 26 | instructions="""You are a helpful email assistant. You can: 27 | - Send emails immediately 28 | - Schedule emails for later 29 | - Help compose professional email content 30 | 31 | Always be polite and professional in your responses, ask the user for confirmation before finalizing.""", 32 | functions=[send_email, schedule_email], 33 | ) 34 | 35 | 36 | def run_demo_loop(starting_agent, debug=False) -> None: 37 | client = Orchestrator() 38 | print("Starting Email Assistant CLI") 39 | 40 | messages = [] 41 | agent = starting_agent 42 | 43 | while True: 44 | user_input = input("\033[90mUser\033[0m: (Enter 'q' to quit) ") 45 | if user_input.lower() == "q": 46 | break 47 | 48 | messages.append({"role": "user", "content": user_input}) 49 | 50 | response = client.run( 51 | agent=agent, 52 | messages=messages, 53 | debug=debug, 54 | ) 55 | 56 | print(f'{response.agent.name}: {response.messages[-1]["content"]}') 57 | messages.extend(response.messages) 58 | agent = response.agent 59 | 60 | 61 | if __name__ == "__main__": 62 | run_demo_loop(email_agent, debug=True) 63 | -------------------------------------------------------------------------------- /src/examples/triage_agent.py: -------------------------------------------------------------------------------- 1 | from src.core import Orchestrator, Agent 2 | 3 | 4 | def process_refund(item_id, reason="NOT SPECIFIED"): 5 | """Refund an item. Refund an item. Make sure you have the item_id of the form item_... Ask for user confirmation before processing the refund.""" 6 | print(f"[mock] Refunding item {item_id} because {reason}...") 7 | return "Success!" 8 | 9 | 10 | def apply_discount(): 11 | """Apply a discount to the user's cart.""" 12 | print("[mock] Applying discount...") 13 | return "Applied discount of 11%" 14 | 15 | 16 | triage_agent = Agent( 17 | name="Triage Agent", 18 | instructions="Determine which agent is best suited to handle the user's request, and transfer the conversation to that agent.", 19 | ) 20 | sales_agent = Agent( 21 | name="Sales Agent", 22 | instructions="Be super enthusiastic about selling bees.", 23 | ) 24 | refunds_agent = Agent( 25 | name="Refunds Agent", 26 | instructions="Help the user with a refund. If the reason is that it was too expensive, offer the user a refund code. If they insist, then process the refund.", 27 | functions=[process_refund, apply_discount], 28 | ) 29 | 30 | 31 | def transfer_back_to_triage(): 32 | """Call this function if a user is asking about a topic that is not handled by the current agent.""" 33 | return triage_agent 34 | 35 | 36 | def transfer_to_sales(): 37 | return sales_agent 38 | 39 | 40 | def transfer_to_refunds(): 41 | return refunds_agent 42 | 43 | 44 | triage_agent.functions = [transfer_to_sales, transfer_to_refunds] 45 | sales_agent.functions.append(transfer_back_to_triage) 46 | refunds_agent.functions.append(transfer_back_to_triage) 47 | 48 | 49 | def run_demo_loop(starting_agent, debug=False) -> None: 50 | client = Orchestrator() 51 | print("Starting Email Assistant CLI") 52 | 53 | messages = [] 54 | agent = starting_agent 55 | 56 | while True: 57 | user_input = input("\033[90mUser\033[0m: (Enter 'q' to quit) ") 58 | if user_input.lower() == "q": 59 | break 60 | 61 | messages.append({"role": "user", "content": user_input}) 62 | 63 | response = client.run( 64 | agent=agent, 65 | messages=messages, 66 | debug=debug, 67 | ) 68 | 69 | print(f'{response.agent.name}: {response.messages[-1]["content"]}') 70 | messages.extend(response.messages) 71 | agent = response.agent 72 | 73 | 74 | if __name__ == "__main__": 75 | run_demo_loop(triage_agent, debug=True) 76 | -------------------------------------------------------------------------------- /src/core.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import json 3 | 4 | from openai import OpenAI 5 | 6 | from src.utils import function_to_json, debug_print 7 | from src.schemas import ( 8 | Agent, 9 | AgentFunction, 10 | ChatCompletionMessage, 11 | ChatCompletionMessageToolCall, 12 | Response, 13 | Result, 14 | ) 15 | from src.configs import settings 16 | 17 | 18 | class Orchestrator: 19 | def __init__(self, client: OpenAI = None): 20 | self.client = client or OpenAI(api_key=settings.OPENAI_API_KEY) 21 | 22 | def get_completion( 23 | self, agent: Agent, history: list, debug: bool 24 | ) -> ChatCompletionMessage: 25 | messages = [{"role": "system", "content": agent.instructions}] + history 26 | debug_print(debug, "Getting chat completion for...:", messages) 27 | 28 | tools = [function_to_json(f) for f in agent.functions] 29 | 30 | params = { 31 | "model": agent.model, 32 | "messages": messages, 33 | "tools": tools or None, 34 | } 35 | 36 | return self.client.chat.completions.create(**params) 37 | 38 | def handle_function_result(self, result, debug: bool) -> Result: 39 | match result: 40 | case Result() as result: 41 | return result 42 | 43 | case Agent() as agent: 44 | return Result( 45 | value=json.dumps({"assistant": agent.name}), 46 | agent=agent, 47 | ) 48 | case _: 49 | try: 50 | return Result(value=str(result)) 51 | except Exception as e: 52 | error_message = f"Failed to cast response to string: {result}. Make sure agent functions return a string or Result object. Error: {str(e)}" 53 | debug_print(debug, error_message) 54 | raise TypeError(error_message) 55 | 56 | def handle_tool_calls( 57 | self, 58 | tool_calls: list[ChatCompletionMessageToolCall], 59 | functions: list[AgentFunction], 60 | debug: bool, 61 | ) -> Response: 62 | function_map = {f.__name__: f for f in functions} 63 | partial_response = Response(messages=[], agent=None) 64 | 65 | for tool_call in tool_calls: 66 | name = tool_call.function.name 67 | 68 | if name not in function_map: 69 | debug_print(debug, f"Tool {name} not found in function map.") 70 | partial_response.messages.append( 71 | { 72 | "role": "tool", 73 | "tool_call_id": tool_call.id, 74 | "tool_name": name, 75 | "content": f"Error: Tool {name} not found.", 76 | } 77 | ) 78 | continue 79 | 80 | args = json.loads(tool_call.function.arguments) 81 | debug_print(debug, f"Processing tool call: {name} with arguments {args}") 82 | 83 | raw_result = function_map[name](**args) 84 | result: Result = self.handle_function_result(raw_result, debug) 85 | partial_response.messages.append( 86 | { 87 | "role": "tool", 88 | "tool_call_id": tool_call.id, 89 | "tool_name": name, 90 | "content": result.value, 91 | } 92 | ) 93 | if result.agent: 94 | partial_response.agent = result.agent 95 | 96 | return partial_response 97 | 98 | def run( 99 | self, 100 | agent: Agent, 101 | messages: list, 102 | max_turns: int = 20, 103 | debug: bool = False, 104 | ): 105 | active_agent = agent 106 | history = copy.deepcopy(messages) 107 | init_len = len(messages) 108 | 109 | while len(history) - init_len < max_turns and active_agent: 110 | completion = self.get_completion( 111 | agent=active_agent, 112 | history=history, 113 | debug=debug, 114 | ) 115 | 116 | message = completion.choices[0].message 117 | debug_print(debug, "Received completion:", message) 118 | message.sender = active_agent.name 119 | history.append(json.loads(message.model_dump_json())) 120 | 121 | if not message.tool_calls: 122 | debug_print(debug, "Ending turn.") 123 | break 124 | 125 | partial_response = self.handle_tool_calls( 126 | message.tool_calls, active_agent.functions, debug=debug 127 | ) 128 | history.extend(partial_response.messages) 129 | if partial_response.agent: 130 | active_agent = partial_response.agent 131 | 132 | return Response( 133 | messages=history[init_len:], 134 | agent=active_agent, 135 | ) 136 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Database ### 3 | *.accdb 4 | *.db 5 | *.dbf 6 | *.mdb 7 | *.pdb 8 | *.sqlite3 9 | *.db-shm 10 | *.db-wal 11 | 12 | ### dotenv ### 13 | .env 14 | 15 | ### Python ### 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | backend/logs 25 | 26 | # Distribution / packaging 27 | .Python 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | share/python-wheels/ 40 | *.egg-info/ 41 | .installed.cfg 42 | *.egg 43 | MANIFEST 44 | 45 | # PyInstaller 46 | # Usually these files are written by a python script from a template 47 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 48 | *.manifest 49 | *.spec 50 | 51 | .env.local 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .nox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | *.py,cover 68 | .hypothesis/ 69 | .pytest_cache/ 70 | cover/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | db.sqlite3 80 | db.sqlite3-journal 81 | 82 | # Flask stuff: 83 | instance/ 84 | .webassets-cache 85 | 86 | # Scrapy stuff: 87 | .scrapy 88 | 89 | # Sphinx documentation 90 | docs/_build/ 91 | 92 | # PyBuilder 93 | .pybuilder/ 94 | target/ 95 | 96 | # Jupyter Notebook 97 | .ipynb_checkpoints 98 | 99 | # IPython 100 | profile_default/ 101 | ipython_config.py 102 | 103 | # pyenv 104 | # For a library or package, you might want to ignore these files since the code is 105 | # intended to run in multiple environments; otherwise, check them in: 106 | # .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # poetry 116 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 117 | # This is especially recommended for binary packages to ensure reproducibility, and is more 118 | # commonly ignored for libraries. 119 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 120 | #poetry.lock 121 | 122 | # pdm 123 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 124 | #pdm.lock 125 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 126 | # in version control. 127 | # https://pdm.fming.dev/#use-with-ide 128 | .pdm.toml 129 | 130 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 131 | __pypackages__/ 132 | 133 | # Celery stuff 134 | celerybeat-schedule 135 | celerybeat.pid 136 | 137 | # SageMath parsed files 138 | *.sage.py 139 | 140 | # Environments 141 | .venv 142 | env/ 143 | venv/ 144 | ENV/ 145 | env.bak/ 146 | venv.bak/ 147 | 148 | # Spyder project settings 149 | .spyderproject 150 | .spyproject 151 | 152 | # Rope project settings 153 | .ropeproject 154 | 155 | # mkdocs documentation 156 | /site 157 | 158 | # mypy 159 | .mypy_cache/ 160 | .dmypy.json 161 | dmypy.json 162 | 163 | # Pyre type checker 164 | .pyre/ 165 | 166 | # pytype static type analyzer 167 | .pytype/ 168 | 169 | # Cython debug symbols 170 | cython_debug/ 171 | 172 | # PyCharm 173 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 174 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 175 | # and can be added to the global gitignore or merged into this file. For a more nuclear 176 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 177 | #.idea/ 178 | 179 | ### Python Patch ### 180 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 181 | poetry.toml 182 | 183 | # ruff 184 | .ruff_cache/ 185 | 186 | # LSP config files 187 | pyrightconfig.json 188 | 189 | ### Redis ### 190 | # Ignore redis binary dump (dump.rdb) files 191 | 192 | *.rdb 193 | 194 | ### venv ### 195 | # Virtualenv 196 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 197 | [Bb]in 198 | [Ii]nclude 199 | [Ll]ib64 200 | [Ll]ocal 201 | pyvenv.cfg 202 | pip-selfcheck.json 203 | .env 204 | *.env 205 | *.txt 206 | 207 | ## csv 208 | *.csv 209 | 210 | ### VisualStudioCode ### 211 | .vscode/* 212 | !.vscode/settings.json 213 | !.vscode/tasks.json 214 | !.vscode/launch.json 215 | !.vscode/extensions.json 216 | !.vscode/*.code-snippets 217 | .DS_Store 218 | 219 | # Local History for Visual Studio Code 220 | .history/ 221 | 222 | # Built Visual Studio Code Extensions 223 | *.vsix 224 | 225 | ### VisualStudioCode Patch ### 226 | # Ignore all local history of files 227 | .history 228 | .ionide 229 | 230 | ## Reptitve test files for local development 231 | tmp 232 | qdrant_storage 233 | notebooks/dump 234 | notebooks/models 235 | notebooks/logs 236 | 237 | # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,venv,dotenv,redis,database -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "annotated-types" 5 | version = "0.7.0" 6 | description = "Reusable constraint types to use with typing.Annotated" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 11 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.8.0" 17 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 18 | optional = false 19 | python-versions = ">=3.9" 20 | files = [ 21 | {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, 22 | {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, 23 | ] 24 | 25 | [package.dependencies] 26 | idna = ">=2.8" 27 | sniffio = ">=1.1" 28 | typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} 29 | 30 | [package.extras] 31 | doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] 32 | test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] 33 | trio = ["trio (>=0.26.1)"] 34 | 35 | [[package]] 36 | name = "certifi" 37 | version = "2024.12.14" 38 | description = "Python package for providing Mozilla's CA Bundle." 39 | optional = false 40 | python-versions = ">=3.6" 41 | files = [ 42 | {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, 43 | {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, 44 | ] 45 | 46 | [[package]] 47 | name = "colorama" 48 | version = "0.4.6" 49 | description = "Cross-platform colored terminal text." 50 | optional = false 51 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 52 | files = [ 53 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 54 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 55 | ] 56 | 57 | [[package]] 58 | name = "distro" 59 | version = "1.9.0" 60 | description = "Distro - an OS platform information API" 61 | optional = false 62 | python-versions = ">=3.6" 63 | files = [ 64 | {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, 65 | {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, 66 | ] 67 | 68 | [[package]] 69 | name = "h11" 70 | version = "0.14.0" 71 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 72 | optional = false 73 | python-versions = ">=3.7" 74 | files = [ 75 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 76 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 77 | ] 78 | 79 | [[package]] 80 | name = "httpcore" 81 | version = "1.0.7" 82 | description = "A minimal low-level HTTP client." 83 | optional = false 84 | python-versions = ">=3.8" 85 | files = [ 86 | {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, 87 | {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, 88 | ] 89 | 90 | [package.dependencies] 91 | certifi = "*" 92 | h11 = ">=0.13,<0.15" 93 | 94 | [package.extras] 95 | asyncio = ["anyio (>=4.0,<5.0)"] 96 | http2 = ["h2 (>=3,<5)"] 97 | socks = ["socksio (==1.*)"] 98 | trio = ["trio (>=0.22.0,<1.0)"] 99 | 100 | [[package]] 101 | name = "httpx" 102 | version = "0.28.1" 103 | description = "The next generation HTTP client." 104 | optional = false 105 | python-versions = ">=3.8" 106 | files = [ 107 | {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, 108 | {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, 109 | ] 110 | 111 | [package.dependencies] 112 | anyio = "*" 113 | certifi = "*" 114 | httpcore = "==1.*" 115 | idna = "*" 116 | 117 | [package.extras] 118 | brotli = ["brotli", "brotlicffi"] 119 | cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] 120 | http2 = ["h2 (>=3,<5)"] 121 | socks = ["socksio (==1.*)"] 122 | zstd = ["zstandard (>=0.18.0)"] 123 | 124 | [[package]] 125 | name = "idna" 126 | version = "3.10" 127 | description = "Internationalized Domain Names in Applications (IDNA)" 128 | optional = false 129 | python-versions = ">=3.6" 130 | files = [ 131 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 132 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 133 | ] 134 | 135 | [package.extras] 136 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 137 | 138 | [[package]] 139 | name = "jiter" 140 | version = "0.8.2" 141 | description = "Fast iterable JSON parser." 142 | optional = false 143 | python-versions = ">=3.8" 144 | files = [ 145 | {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, 146 | {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, 147 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d"}, 148 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66"}, 149 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5"}, 150 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3"}, 151 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08"}, 152 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49"}, 153 | {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d"}, 154 | {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff"}, 155 | {file = "jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43"}, 156 | {file = "jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105"}, 157 | {file = "jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b"}, 158 | {file = "jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15"}, 159 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0"}, 160 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f"}, 161 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099"}, 162 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74"}, 163 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586"}, 164 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc"}, 165 | {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88"}, 166 | {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6"}, 167 | {file = "jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44"}, 168 | {file = "jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855"}, 169 | {file = "jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f"}, 170 | {file = "jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44"}, 171 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f"}, 172 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60"}, 173 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57"}, 174 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e"}, 175 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887"}, 176 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d"}, 177 | {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152"}, 178 | {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29"}, 179 | {file = "jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e"}, 180 | {file = "jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c"}, 181 | {file = "jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84"}, 182 | {file = "jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4"}, 183 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587"}, 184 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c"}, 185 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18"}, 186 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6"}, 187 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef"}, 188 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1"}, 189 | {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9"}, 190 | {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05"}, 191 | {file = "jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a"}, 192 | {file = "jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865"}, 193 | {file = "jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca"}, 194 | {file = "jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0"}, 195 | {file = "jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566"}, 196 | {file = "jiter-0.8.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c"}, 197 | {file = "jiter-0.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c"}, 198 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b"}, 199 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5"}, 200 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e"}, 201 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8"}, 202 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817"}, 203 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1"}, 204 | {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6"}, 205 | {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7"}, 206 | {file = "jiter-0.8.2-cp38-cp38-win32.whl", hash = "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63"}, 207 | {file = "jiter-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6"}, 208 | {file = "jiter-0.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee"}, 209 | {file = "jiter-0.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc"}, 210 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d"}, 211 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c"}, 212 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d"}, 213 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9"}, 214 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4"}, 215 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27"}, 216 | {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841"}, 217 | {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637"}, 218 | {file = "jiter-0.8.2-cp39-cp39-win32.whl", hash = "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36"}, 219 | {file = "jiter-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a"}, 220 | {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, 221 | ] 222 | 223 | [[package]] 224 | name = "markdown-it-py" 225 | version = "3.0.0" 226 | description = "Python port of markdown-it. Markdown parsing, done right!" 227 | optional = false 228 | python-versions = ">=3.8" 229 | files = [ 230 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 231 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 232 | ] 233 | 234 | [package.dependencies] 235 | mdurl = ">=0.1,<1.0" 236 | 237 | [package.extras] 238 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 239 | code-style = ["pre-commit (>=3.0,<4.0)"] 240 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 241 | linkify = ["linkify-it-py (>=1,<3)"] 242 | plugins = ["mdit-py-plugins"] 243 | profiling = ["gprof2dot"] 244 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 245 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 246 | 247 | [[package]] 248 | name = "mdurl" 249 | version = "0.1.2" 250 | description = "Markdown URL utilities" 251 | optional = false 252 | python-versions = ">=3.7" 253 | files = [ 254 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 255 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 256 | ] 257 | 258 | [[package]] 259 | name = "openai" 260 | version = "1.60.1" 261 | description = "The official Python library for the openai API" 262 | optional = false 263 | python-versions = ">=3.8" 264 | files = [ 265 | {file = "openai-1.60.1-py3-none-any.whl", hash = "sha256:714181ec1c452353d456f143c22db892de7b373e3165063d02a2b798ed575ba1"}, 266 | {file = "openai-1.60.1.tar.gz", hash = "sha256:beb1541dfc38b002bd629ab68b0d6fe35b870c5f4311d9bc4404d85af3214d5e"}, 267 | ] 268 | 269 | [package.dependencies] 270 | anyio = ">=3.5.0,<5" 271 | distro = ">=1.7.0,<2" 272 | httpx = ">=0.23.0,<1" 273 | jiter = ">=0.4.0,<1" 274 | pydantic = ">=1.9.0,<3" 275 | sniffio = "*" 276 | tqdm = ">4" 277 | typing-extensions = ">=4.11,<5" 278 | 279 | [package.extras] 280 | datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] 281 | realtime = ["websockets (>=13,<15)"] 282 | 283 | [[package]] 284 | name = "pydantic" 285 | version = "2.10.6" 286 | description = "Data validation using Python type hints" 287 | optional = false 288 | python-versions = ">=3.8" 289 | files = [ 290 | {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, 291 | {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, 292 | ] 293 | 294 | [package.dependencies] 295 | annotated-types = ">=0.6.0" 296 | pydantic-core = "2.27.2" 297 | typing-extensions = ">=4.12.2" 298 | 299 | [package.extras] 300 | email = ["email-validator (>=2.0.0)"] 301 | timezone = ["tzdata"] 302 | 303 | [[package]] 304 | name = "pydantic-core" 305 | version = "2.27.2" 306 | description = "Core functionality for Pydantic validation and serialization" 307 | optional = false 308 | python-versions = ">=3.8" 309 | files = [ 310 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, 311 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, 312 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, 313 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, 314 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, 315 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, 316 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, 317 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, 318 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, 319 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, 320 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, 321 | {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, 322 | {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, 323 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, 324 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, 325 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, 326 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, 327 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, 328 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, 329 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, 330 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, 331 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, 332 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, 333 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, 334 | {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, 335 | {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, 336 | {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, 337 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, 338 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, 339 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, 340 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, 341 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, 342 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, 343 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, 344 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, 345 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, 346 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, 347 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, 348 | {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, 349 | {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, 350 | {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, 351 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, 352 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, 353 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, 354 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, 355 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, 356 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, 357 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, 358 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, 359 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, 360 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, 361 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, 362 | {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, 363 | {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, 364 | {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, 365 | {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, 366 | {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, 367 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, 368 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, 369 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, 370 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, 371 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, 372 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, 373 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, 374 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, 375 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, 376 | {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, 377 | {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, 378 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, 379 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, 380 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, 381 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, 382 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, 383 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, 384 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, 385 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, 386 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, 387 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, 388 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, 389 | {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, 390 | {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, 391 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, 392 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, 393 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, 394 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, 395 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, 396 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, 397 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, 398 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, 399 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, 400 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, 401 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, 402 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, 403 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, 404 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, 405 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, 406 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, 407 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, 408 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, 409 | {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, 410 | ] 411 | 412 | [package.dependencies] 413 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 414 | 415 | [[package]] 416 | name = "pydantic-settings" 417 | version = "2.7.1" 418 | description = "Settings management using Pydantic" 419 | optional = false 420 | python-versions = ">=3.8" 421 | files = [ 422 | {file = "pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd"}, 423 | {file = "pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93"}, 424 | ] 425 | 426 | [package.dependencies] 427 | pydantic = ">=2.7.0" 428 | python-dotenv = ">=0.21.0" 429 | 430 | [package.extras] 431 | azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] 432 | toml = ["tomli (>=2.0.1)"] 433 | yaml = ["pyyaml (>=6.0.1)"] 434 | 435 | [[package]] 436 | name = "pygments" 437 | version = "2.19.1" 438 | description = "Pygments is a syntax highlighting package written in Python." 439 | optional = false 440 | python-versions = ">=3.8" 441 | files = [ 442 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 443 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 444 | ] 445 | 446 | [package.extras] 447 | windows-terminal = ["colorama (>=0.4.6)"] 448 | 449 | [[package]] 450 | name = "python-dotenv" 451 | version = "1.0.1" 452 | description = "Read key-value pairs from a .env file and set them as environment variables" 453 | optional = false 454 | python-versions = ">=3.8" 455 | files = [ 456 | {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, 457 | {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, 458 | ] 459 | 460 | [package.extras] 461 | cli = ["click (>=5.0)"] 462 | 463 | [[package]] 464 | name = "rich" 465 | version = "13.9.4" 466 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 467 | optional = false 468 | python-versions = ">=3.8.0" 469 | files = [ 470 | {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, 471 | {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, 472 | ] 473 | 474 | [package.dependencies] 475 | markdown-it-py = ">=2.2.0" 476 | pygments = ">=2.13.0,<3.0.0" 477 | 478 | [package.extras] 479 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 480 | 481 | [[package]] 482 | name = "sniffio" 483 | version = "1.3.1" 484 | description = "Sniff out which async library your code is running under" 485 | optional = false 486 | python-versions = ">=3.7" 487 | files = [ 488 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 489 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 490 | ] 491 | 492 | [[package]] 493 | name = "tqdm" 494 | version = "4.67.1" 495 | description = "Fast, Extensible Progress Meter" 496 | optional = false 497 | python-versions = ">=3.7" 498 | files = [ 499 | {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, 500 | {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, 501 | ] 502 | 503 | [package.dependencies] 504 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 505 | 506 | [package.extras] 507 | dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] 508 | discord = ["requests"] 509 | notebook = ["ipywidgets (>=6)"] 510 | slack = ["slack-sdk"] 511 | telegram = ["requests"] 512 | 513 | [[package]] 514 | name = "typing-extensions" 515 | version = "4.12.2" 516 | description = "Backported and Experimental Type Hints for Python 3.8+" 517 | optional = false 518 | python-versions = ">=3.8" 519 | files = [ 520 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 521 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 522 | ] 523 | 524 | [metadata] 525 | lock-version = "2.0" 526 | python-versions = "^3.11" 527 | content-hash = "eabe7bbace923192b3c9349e5b7fbdc6c007b65d38f9d49cb963e7408a5d424c" 528 | --------------------------------------------------------------------------------