From 4d4764302a6d26324c83ddb3cbe83efded8cf270 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Fri, 3 Oct 2025 15:04:08 +0200 Subject: [PATCH 1/6] Use litellm --- patterns/__main__.py | 14 +- patterns/app/chaining.py | 23 +- patterns/app/chat.py | 3 +- patterns/app/evaluator_optimizer.py | 3 +- patterns/app/human_in_the_loop.py | 38 +- patterns/app/orchestrator_workers.py | 3 +- ...{parallelization.py => parallel_agents.py} | 5 +- patterns/app/parallel_tools.py | 72 ++ patterns/app/routing_to_agent.py | 156 ++- patterns/app/routing_to_remote_agent.py | 140 +++ patterns/app/routing_to_tool.py | 139 +-- patterns/app/util/__init__.py | 0 patterns/app/util/litellm_call.py | 44 + patterns/{ => app/util}/util.py | 120 +-- patterns/pyproject.toml | 3 +- patterns/uv.lock | 943 +++++++++++++++++- 16 files changed, 1426 insertions(+), 280 deletions(-) rename patterns/app/{parallelization.py => parallel_agents.py} (94%) create mode 100644 patterns/app/parallel_tools.py create mode 100644 patterns/app/routing_to_remote_agent.py create mode 100644 patterns/app/util/__init__.py create mode 100644 patterns/app/util/litellm_call.py rename patterns/{ => app/util}/util.py (65%) diff --git a/patterns/__main__.py b/patterns/__main__.py index 88f0b8a..e716de4 100644 --- a/patterns/__main__.py +++ b/patterns/__main__.py @@ -10,7 +10,12 @@ from app.evaluator_optimizer import evaluator_optimizer from app.human_in_the_loop import content_moderator_svc from app.chat import chat -from app.routing_to_agent import billing_agent, product_agent, account_agent +from app.routing_to_remote_agent import ( + remote_agent_router_service, + billing_agent_svc, + product_agent_svc, + account_agent_svc, +) app = restate.app( services=[ @@ -22,9 +27,10 @@ evaluator_optimizer, content_moderator_svc, chat, - billing_agent, - account_agent, - product_agent, + remote_agent_router_service, + billing_agent_svc, + account_agent_svc, + product_agent_svc, ] ) diff --git a/patterns/app/chaining.py b/patterns/app/chaining.py index 3725267..6412ee2 100644 --- a/patterns/app/chaining.py +++ b/patterns/app/chaining.py @@ -1,7 +1,7 @@ import restate from pydantic import BaseModel -from util import llm_call +from .util.litellm_call import llm_call """ LLM Prompt Chaining @@ -14,18 +14,13 @@ call_chaining_svc = restate.Service("CallChainingService") -# Example input text to analyze -example_prompt = ( - "Q3 Performance Summary: " + +class Prompt(BaseModel): + message: str = "Q3 Performance Summary: " "Our customer satisfaction score rose to 92 points this quarter. " "Revenue grew by 45% compared to last year. " "Market share is now at 23% in our primary market. " "Customer churn decreased to 5% from 8%." -) - - -class Prompt(BaseModel): - message: str = example_prompt @call_chaining_svc.handler() @@ -38,11 +33,11 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: llm_call, restate.RunOptions(max_attempts=3), prompt=f"Extract only the numerical values and their associated metrics from the text. " - f"Format each as 'metric name: metric' on a new line. Input: {prompt}", + f"Format each as 'metric name: metric' on a new line. Input: {prompt.message}", ) # Step 2: Process the result from Step 1 - result = await ctx.run_typed( + result2 = await ctx.run_typed( "Sort metrics", llm_call, restate.RunOptions(max_attempts=3), @@ -50,11 +45,9 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: ) # Step 3: Process the result from Step 2 - result = await ctx.run_typed( + return await ctx.run_typed( "Format as table", llm_call, restate.RunOptions(max_attempts=3), - prompt=f"Format the sorted data as a markdown table with columns 'Metric Name' and 'Value'. Input: {result}", + prompt=f"Format the sorted data as a markdown table with columns 'Metric Name' and 'Value'. Input: {result2}", ) - - return result diff --git a/patterns/app/chat.py b/patterns/app/chat.py index df59f18..43f404e 100644 --- a/patterns/app/chat.py +++ b/patterns/app/chat.py @@ -1,7 +1,7 @@ import restate from pydantic import BaseModel -from util import llm_call +from .util.litellm_call import llm_call chat = restate.VirtualObject("Chat") @@ -25,7 +25,6 @@ async def message(ctx: restate.ObjectContext, prompt: Prompt) -> str: "LLM call", llm_call, restate.RunOptions(max_attempts=3), - prompt=prompt.message, messages=memory, ) diff --git a/patterns/app/evaluator_optimizer.py b/patterns/app/evaluator_optimizer.py index dfa59d9..6f37154 100644 --- a/patterns/app/evaluator_optimizer.py +++ b/patterns/app/evaluator_optimizer.py @@ -1,6 +1,7 @@ import restate from pydantic import BaseModel -from util import llm_call, print_evaluation +from .util.litellm_call import llm_call +from .util.util import print_evaluation """ LLM Iterative Improvement diff --git a/patterns/app/human_in_the_loop.py b/patterns/app/human_in_the_loop.py index a0a1873..18e61b6 100644 --- a/patterns/app/human_in_the_loop.py +++ b/patterns/app/human_in_the_loop.py @@ -1,7 +1,8 @@ import restate -from pydantic import BaseModel +from restate import RunOptions -from util import llm_call, notify_moderator +from .util.litellm_call import llm_call +from .util.util import notify_moderator, Content """ Human-in-the-loop workflows with Restate @@ -9,28 +10,35 @@ Implement resilient human approval steps that suspend execution until feedback is received. These durable promises survive crashes and can be recovered on other processes on retries. """ - content_moderator_svc = restate.Service("HumanInTheLoopService") -class Content(BaseModel): - message: str = "Very explicit content that clearly violates policy." - - @content_moderator_svc.handler() async def moderate(ctx: restate.Context, content: Content) -> str: """Restate service handler as the durable entry point for content moderation.""" # Durable step for LLM inference, auto retried & recovered - analysis = await ctx.run_typed( - "analyze_content", + result = await ctx.run_typed( + "moderate", llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"Analyze content for policy violations. Return 'needsHumanReview' or your decision: {content}", + RunOptions(max_attempts=3), + prompt=f"Decide whether content violates the policy." + f"Use the human review tool if you are not sure." + f"Content: {content}", + tools=[ + { + "type": "function", + "function": { + "name": "get_human_review", + "description": "Get human review for content that may violate policy.", + }, + } + ], ) + result.append(result.model_dump()) - # Simple parsing - in real implementation you'd use proper JSON parsing - if "needsHumanReview" in analysis: + # Otherwise, handle each tool call + if result.tool_calls and result.tool_calls[0].function.name == "get_human_review": # Create a durable promise (awakeable), approval_id, approval_promise = ctx.awakeable(type_hint=str) @@ -38,7 +46,7 @@ async def moderate(ctx: restate.Context, content: Content) -> str: await ctx.run_typed( "notify_moderator", notify_moderator, - content=content.message, + content=content, approval_id=approval_id, ) @@ -48,4 +56,4 @@ async def moderate(ctx: restate.Context, content: Content) -> str: # curl http://localhost:8080/restate/awakeables/sign_.../resolve --json '"approved"' return await approval_promise - return analysis + return result.content diff --git a/patterns/app/orchestrator_workers.py b/patterns/app/orchestrator_workers.py index a4b01ed..bc1db80 100644 --- a/patterns/app/orchestrator_workers.py +++ b/patterns/app/orchestrator_workers.py @@ -2,7 +2,8 @@ from pydantic import BaseModel -from util import llm_call, parse_instructions +from .util.litellm_call import llm_call +from .util.util import parse_instructions """ LLM Orchestrator-Workers diff --git a/patterns/app/parallelization.py b/patterns/app/parallel_agents.py similarity index 94% rename from patterns/app/parallelization.py rename to patterns/app/parallel_agents.py index eb4f822..383a535 100644 --- a/patterns/app/parallelization.py +++ b/patterns/app/parallel_agents.py @@ -1,7 +1,7 @@ import restate -from anthropic import BaseModel +from pydantic import BaseModel -from util import llm_call +from .util.litellm_call import llm_call """ LLM Parallel Processing @@ -58,4 +58,5 @@ async def analyze_text(ctx: restate.Context, prompt: Prompt) -> list[str]: # Wait for all tasks to complete results = await restate.gather(sentiment_task, key_points_task, summary_task) + # Gather and collect results return [await result for result in results] diff --git a/patterns/app/parallel_tools.py b/patterns/app/parallel_tools.py new file mode 100644 index 0000000..601bd0c --- /dev/null +++ b/patterns/app/parallel_tools.py @@ -0,0 +1,72 @@ +import restate +from pydantic import BaseModel +from restate import Context, RunOptions + +from app.util.litellm_call import llm_call +from app.util.util import fetch_weather, WeatherRequest + +manual_loop_agent = restate.Service("ParallelToolAgent") + +get_weather = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather in a given location", + }, +} + + +class MultiWeatherPrompt(BaseModel): + message: str = "What is the weather like in New York, San Francisco, and Boston?" + + +@manual_loop_agent.handler() +async def run(ctx: Context, prompt: MultiWeatherPrompt) -> str: + """Main agent loop with tool calling""" + messages = [{"role": "user", "content": prompt.message}] + + while True: + # Call OpenAI with durable execution + response = await ctx.run_typed( + "llm-call", + llm_call, + RunOptions(max_attempts=3), + messages=messages, + tools=[get_weather], + ) + + assistant_message = response.choices[0].message + messages.append(assistant_message) + + if not assistant_message.tool_calls: + return assistant_message.content + + # start all parallel tool calls with retries and recovery + tool_output_promises = [] + for tool_call in assistant_message.tool_calls: + if tool_call.function.name == "get_weather": + tool_promise = ctx.run_typed( + "Get weather", + fetch_weather, + req=WeatherRequest.model_validate_json( + tool_call.function.arguments + ), + ) + tool_output_promises.append( + {"id": tool_call.id, "promise": tool_promise} + ) + + # wait for all tool calls to complete + await restate.gather(*tool_output_promises) + + # gather the results and add to messages + for tool_output_promise in tool_output_promises: + tool_output = await tool_output_promise["promise"] + # Add tool response to messages + messages.append( + { + "role": "tool", + "tool_call_id": tool_output_promise["id"], + "content": tool_output, + } + ) diff --git a/patterns/app/routing_to_agent.py b/patterns/app/routing_to_agent.py index 53e9d95..99ea6ab 100644 --- a/patterns/app/routing_to_agent.py +++ b/patterns/app/routing_to_agent.py @@ -1,6 +1,6 @@ import restate -import json -from util import llm_call + +from .util.litellm_call import llm_call from pydantic import BaseModel """ @@ -12,14 +12,6 @@ Request → Classifier → Agent A/B/C → Specialized Response """ - -# Specialized agent service names -AGENTS = { - "billing": "BillingAgent", - "account": "AccountAgent", - "product": "ProductAgent", -} - # Example input text to analyze example_prompt = "I can't log into my account. Keep getting invalid password errors." """ @@ -39,89 +31,79 @@ class Prompt(BaseModel): agent_router_service = restate.Service("AgentRouterService") -@agent_router_service.handler() -async def route(ctx: restate.Context, prompt: Prompt) -> str: - """Classify request and route to appropriate specialized agent.""" - - # Classify the request - route_key = await ctx.run_typed( - "classify_request", - llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"""Classify this support request into one category: {AGENTS.keys()} - - billing: payments, charges, refunds, plans - account: login, password, security, access - product: features, usage, best practices - - Reply with only the category name. - - Request: {prompt.message}""", - ) - category = route_key.strip().lower() - agent_service = AGENTS.get(category) or "ProductAgent" - - # Route to specialized agent - response = await ctx.generic_call( - agent_service, - f"get_{category}_support", - arg=json.dumps(prompt.message).encode("utf-8"), - ) - - return response.decode("utf-8") - - -# SPECIALIZED AGENT SERVICES - -# Billing Support Agent -billing_agent = restate.Service("BillingAgent") - - -@billing_agent.handler() -async def get_billing_support(ctx: restate.Context, prompt: str) -> str: - return await ctx.run_typed( - "billing_response", - llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"""You are a billing support specialist. - Acknowledge the billing issue, explain charges clearly, provide next steps with timeline. - Keep responses professional but friendly. +billing_agent = { + "type": "function", + "function": { + "name": "billing_support", + "description": "Handle billing related queries: payments, charges, refunds, plans", + }, +} - Input: {prompt}""", - ) +account_agent = { + "type": "function", + "function": { + "name": "account_support", + "description": "Handle account related queries: login, password, security, access", + }, +} +product_agent = { + "type": "function", + "function": { + "name": "product_support", + "description": "Handle product related queries: features, usage, best practices", + }, +} -# Account Security Agent -account_agent = restate.Service("AccountAgent") +@agent_router_service.handler() +async def route(ctx: restate.Context, prompt: Prompt) -> str: + """Classify request and route to appropriate specialized agent.""" -@account_agent.handler() -async def get_account_support(ctx: restate.Context, prompt: str) -> str: - return await ctx.run_typed( - "account_response", + # Classify the request + result = await ctx.run_typed( + "handle request", llm_call, restate.RunOptions(max_attempts=3), - prompt=f"""You are an account security specialist. - Prioritize account security and verification, provide clear recovery steps, include security tips. - Maintain a serious, security-focused tone. - - Input: {prompt}""", + prompt=prompt.message, + tools=[billing_agent, account_agent, product_agent], ) - -# Product Support Agent -product_agent = restate.Service("ProductAgent") - - -@product_agent.handler() -async def get_product_support(ctx: restate.Context, prompt: str) -> str: - return await ctx.run_typed( - "product_response", - llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"""You are a product specialist. - Focus on feature education and best practices, include specific examples, suggest related features. - Be educational and encouraging in tone. - - Input: {prompt}""", - ) + if not result.tool_calls: + return result.content + + tool_call = result.tool_calls[0] + fn = tool_call.function + # Route to appropriate support tool + if fn.name == "billing_support": + return await ctx.run_typed( + "run billing agent", + llm_call, + restate.RunOptions(max_attempts=3), + system="You are a billing support specialist." + "Acknowledge the billing issue, explain charges clearly, provide next steps with timeline." + "Keep responses professional but friendly.", + prompt=prompt.message, + ).content + elif fn.name == "account_support": + return await ctx.run_typed( + "run account agent", + llm_call, + restate.RunOptions(max_attempts=3), + system="You are an account security specialist." + "Prioritize account security and verification, provide clear recovery steps, include security tips." + "Maintain a serious, security-focused tone.", + prompt=prompt.message, + ).content + elif fn.name == "product_support": + return await ctx.run_typed( + "run product agent", + llm_call, + restate.RunOptions(max_attempts=3), + system="You are a product specialist." + "Focus on feature education and best practices, include specific examples, suggest related features." + "Be educational and encouraging in tone.", + prompt=prompt.message, + ).content + else: + return "Sorry, I couldn't answer your request." diff --git a/patterns/app/routing_to_remote_agent.py b/patterns/app/routing_to_remote_agent.py new file mode 100644 index 0000000..ff4dee9 --- /dev/null +++ b/patterns/app/routing_to_remote_agent.py @@ -0,0 +1,140 @@ +import restate +import json +from .util.litellm_call import llm_call +from pydantic import BaseModel + +""" +LLM Request Routing + +Automatically route requests to specialized agents based on content analysis. +Each route is handled by a dedicated agent service with domain expertise. + +Request → Classifier → Agent A/B/C → Specialized Response +""" + +# Example input text to analyze +example_prompt = "I can't log into my account. Keep getting invalid password errors." +""" +Other examples: + "Why was I charged $49.99 when I'm on the $29.99 plan?", + "How do I export my project data to Excel format?", + "What's the best way to organize my dashboard widgets?" +""" + + +class Prompt(BaseModel): + message: str = example_prompt + + +# ROUTING AGENT + +remote_agent_router_service = restate.Service("RemoteAgentRouterService") + + +@remote_agent_router_service.handler() +async def route(ctx: restate.Context, prompt: Prompt) -> str: + """Classify request and route to appropriate specialized agent.""" + + # Classify the request + result = await ctx.run_typed( + "handle request", + llm_call, + restate.RunOptions(max_attempts=3), + prompt=prompt.message, + tools=[billing_agent, account_agent, product_agent], + ) + + if not result.tool_calls: + return result.content + + tool_call = result.tool_calls[0] + + # We use a generic call to route to the specialized agent service + # Generic calls let us call agents by string name and method + response = await ctx.generic_call( + tool_call.function.name, + "run", + arg=json.dumps(prompt.message).encode("utf-8"), + ) + return response.decode("utf-8") + + +# SPECIALIZED AGENT SERVICES + +# Billing Support Agent +billing_agent = { + "type": "function", + "function": { + "name": "BillingAgent", + "description": "Handle billing related queries: payments, charges, refunds, plans", + }, +} + + +billing_agent_svc = restate.Service("BillingAgent") + + +@billing_agent_svc.handler("run") +async def get_billing_support(ctx: restate.Context, prompt: str) -> str: + result = await ctx.run_typed( + "billing_response", + llm_call, + restate.RunOptions(max_attempts=3), + system=f"""You are a billing support specialist. + Acknowledge the billing issue, explain charges clearly, provide next steps with timeline. + Keep responses professional but friendly.""", + prompt=prompt, + ) + return result.content + + +# Account Security Agent +account_agent = { + "type": "function", + "function": { + "name": "AccountAgent", + "description": "Handle account related queries: login, password, security, access", + }, +} + +account_agent_svc = restate.Service("AccountAgent") + + +@account_agent_svc.handler("run") +async def get_account_support(ctx: restate.Context, prompt: str) -> str: + result = await ctx.run_typed( + "account_response", + llm_call, + restate.RunOptions(max_attempts=3), + system=f"""You are an account security specialist. + Prioritize account security and verification, provide clear recovery steps, include security tips. + Maintain a serious, security-focused tone.""", + prompt=prompt, + ) + return result.content + + +# Product Support Agent +product_agent = { + "type": "function", + "function": { + "name": "ProductAgent", + "description": "Handle product related queries: features, usage, best practices", + }, +} + +product_agent_svc = restate.Service("ProductAgent") + + +@product_agent_svc.handler("run") +async def get_product_support(ctx: restate.Context, prompt: str) -> str: + result = await ctx.run_typed( + "product_response", + llm_call, + restate.RunOptions(max_attempts=3), + system=f"""You are a product specialist. + Focus on feature education and best practices, include specific examples, suggest related features. + Be educational and encouraging in tone.""", + prompt=prompt, + ) + return result.content diff --git a/patterns/app/routing_to_tool.py b/patterns/app/routing_to_tool.py index 7bec03e..e333a04 100644 --- a/patterns/app/routing_to_tool.py +++ b/patterns/app/routing_to_tool.py @@ -1,11 +1,14 @@ import restate +from litellm.types.utils import Message from pydantic import BaseModel +from restate import RunOptions -from util import ( - llm_call, - fetch_service_status, +from .util.litellm_call import llm_call +from .util.util import ( create_support_ticket, - query_user_database, + fetch_service_status, + query_user_db, + SupportTicket, ) """ @@ -18,8 +21,35 @@ """ -# Available support tool categories -TOOLS = ["user_database", "service_status", "ticket_management"] +# TOOLS +service_status_tool = { + "type": "function", + "function": { + "name": "fetch_service_status", + "description": "Tool for checking service status and outages via internal APIs. " + "Call this to get current status of services, incidents, and uptime.", + }, +} + +create_ticket_tool = { + "type": "function", + "function": { + "name": "create_support_ticket", + "description": "Tool for creating tickets in CRM system for bugs, feature requests, escalations. " + "Call this to log new issues reported by users.", + "parameters": SupportTicket.model_json_schema(), + }, +} + +user_database_tool = { + "type": "function", + "function": { + "name": "query_user_database", + "description": "Get human review for content that may violate policy. " + "Call this for queries about account info, subscription details, " + "usage limits, billing questions, user profile", + }, +} # Example customer support request example_prompt = "My API calls are failing, what's wrong with my account?" @@ -44,63 +74,54 @@ class Prompt(BaseModel): @tool_router_service.handler() async def route(ctx: restate.Context, prompt: Prompt) -> str: """Classify request and route to appropriate tool function.""" + messages = [{"role": "user", "content": prompt.message}] # Classify the customer support request - route_key = await ctx.run_typed( - "classify_request", + result = await ctx.run_typed( + "LLM call", llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"""Classify this customer support request into one category: {list(TOOLS)} - - user_database: account info, subscription details, usage limits, billing questions, user profile - service_status: outages, downtime, service availability, system status, performance issues - ticket_management: bug reports, feature requests, technical issues that need escalation - - Reply with only the category name. - - Request: {prompt.message}""", + RunOptions(max_attempts=3, type_hint=Message), + messages=messages, + tools=[create_ticket_tool, service_status_tool, user_database_tool], ) - - tool_category = route_key.strip().lower() - - # Route to appropriate support tool - if tool_category == "user_database": - tool_result = await user_database_tool(ctx, user_id=prompt.user_id) - elif tool_category == "service_status": - tool_result = await service_status_tool(ctx) - elif tool_category == "ticket_management": - tool_result = await ticket_management_tool(ctx, prompt) - else: - tool_result = f"Didn't find info for {tool_category}" - - response = await ctx.run_typed( + messages.append(result) + + if not result.tool_calls: + return result.content + + for tool_call in result.tool_calls: + fn = tool_call.function + # Route to appropriate support tool + if fn.name == "query_user_database": + tool_result = await ctx.run_typed( + "Query user DB", query_user_db, user_id=prompt.user_id + ) + elif fn.name == "fetch_service_status": + tool_result = await ctx.run_typed( + "Get service status", fetch_service_status + ) + elif fn.name == "create_ticket": + tool_result = await ctx.run_typed( + "create support ticket", + create_support_ticket, + ticket=SupportTicket.model_validate_json(fn.arguments), + ) + else: + tool_result = f"Didn't find tool for {fn.name}" + messages.append( + { + "tool_call_id": tool_call.id, + "role": "tool", + "name": fn.name, + "content": tool_result, + } + ) + + # Final response to user based on tool result + return await ctx.run_typed( "analyze tool output", llm_call, - restate.RunOptions(max_attempts=3), - prompt=f"Provide a concise, friendly response to the user question {prompt} based on this info: {tool_result}", - ) - - return response - - -# CUSTOMER SUPPORT TOOL FUNCTIONS - - -async def user_database_tool(ctx: restate.Context, user_id: str) -> str: - """Tool for querying user database - subscriptions, usage, billing info.""" - return await ctx.run_typed("query_user_db", query_user_database, user_id=user_id) - - -async def service_status_tool(ctx: restate.Context) -> str: - """Tool for checking service status and outages via internal APIs.""" - return await ctx.run_typed("check_service_status", fetch_service_status) - - -async def ticket_management_tool(ctx: restate.Context, request: Prompt) -> str: - """Tool for creating tickets in CRM system for bugs, feature requests, escalations.""" - return await ctx.run_typed( - "create support ticket", - create_support_ticket, - request=request.message, - user_id=request.user_id, + RunOptions(max_attempts=3, type_hint=Message), + prompt=f"Provide a concise, friendly response to the user question {prompt.message} based on the tool output.", + messages=messages, ) diff --git a/patterns/app/util/__init__.py b/patterns/app/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/patterns/app/util/litellm_call.py b/patterns/app/util/litellm_call.py new file mode 100644 index 0000000..e670c60 --- /dev/null +++ b/patterns/app/util/litellm_call.py @@ -0,0 +1,44 @@ +import litellm +from typing import Optional, List + +from litellm.types.utils import Message +from restate import TerminalError + + +def llm_call( + prompt: str = None, + system: str = None, + messages: Optional[list[dict[str, str]]] = None, + tools: Optional[List] = None, +) -> Message: + """ + Calls the model with the given prompt and returns the response. + + Args: + prompt (str): The user prompt to send to the model. + system (str, optional): The system prompt to send to the model. Defaults to "". + messages (list, optional): Previous messages for context in chat models. Defaults to None. + tools (list, optional): List of tools for the model to use. Defaults to None. + + Returns: + str: The response from the language model. + """ + + if not prompt and not messages: + raise TerminalError("Either prompt or messages must be provided.") + + messages = messages or [] + if system: + messages.append({"role": "system", "content": system}) + if prompt: + messages.append({"role": "user", "content": prompt}) + content = ( + litellm.completion(model="gpt-4o", messages=messages, tools=tools) + .choices[0] + .message + ) + + if content: + return content + else: + raise RuntimeError("No content in response") diff --git a/patterns/util.py b/patterns/app/util/util.py similarity index 65% rename from patterns/util.py rename to patterns/app/util/util.py index 2d57c7b..a696949 100644 --- a/patterns/util.py +++ b/patterns/app/util/util.py @@ -1,68 +1,8 @@ import datetime -import logging -import os -from typing import Optional - -import restate -from openai import OpenAI -from anthropic import Anthropic - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -def llm_call( - prompt: str, system: str = "", messages: Optional[list[dict[str, str]]] = None -) -> str: - """ - Calls the model with the given prompt and returns the response. - - Args: - prompt (str): The user prompt to send to the model. - system (str, optional): The system prompt to send to the model. Defaults to "". - messages (list, optional): Previous messages for context in chat models. Defaults to None. - - Returns: - str: The response from the language model. - """ - messages = messages or [] - if system: - messages.append({"role": "system", "content": system}) - if prompt: - messages.append({"role": "user", "content": prompt}) - - if os.getenv("OPENAI_API_KEY"): - openai_client = OpenAI() - openai_response = openai_client.chat.completions.create( - model="gpt-4o", - max_tokens=4096, - messages=messages, - temperature=0.1, - ) - content = openai_response.choices[0].message.content - if content: - return content - else: - raise RuntimeError("No content in OpenAI response") - elif os.getenv("ANTHROPIC_API_KEY"): - anthropic_client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) - anthropic_response = anthropic_client.messages.create( - model="claude-3-5-sonnet-latest", - max_tokens=4096, - system=system, - messages=messages, - temperature=0.1, - ) - if anthropic_response.content[0].type == "text": - return anthropic_response.content[0].text - else: - raise RuntimeError( - f"Unexpected response type: {anthropic_response.content[0].type}" - ) - else: - raise restate.TerminalError( - "Missing API key: set either the env var OPENAI_API_KEY or ANTHROPIC_API_KEY" - ) + +import httpx +from pydantic import BaseModel +from restate import TerminalError def print_evaluation(iteration: int, solution: str, evaluation: str): @@ -120,16 +60,21 @@ def fetch_service_status() -> str: ) -def create_support_ticket(request: str, user_id: str) -> str: +class SupportTicket(BaseModel): + user_id: str + message: str + + +def create_support_ticket(ticket: SupportTicket) -> str: # Mock ticket creation (would be real API calls to ticketing systems) ticket_id = "TICKET-" + str(abs(hash(request)) % 10000) return str( { "ticket_id": ticket_id, - "user_id": user_id, + "user_id": ticket.user_id, "status": "open", "created_at": datetime.datetime.now().isoformat(), - "details": request, + "details": ticket.message, } ) @@ -177,7 +122,7 @@ def create_support_ticket(request: str, user_id: str) -> str: } -def query_user_database(user_id: str) -> str: +def query_user_db(user_id: str) -> str: content = users_db.get(user_id, None) if content: return str(content) @@ -202,10 +147,18 @@ def parse_instructions(task_breakdown: str) -> dict: return worker_instructions -def notify_moderator(content: str, approval_id: str): +class HumanReviewRequest(BaseModel): + request: str + + +class Content(BaseModel): + message: str = "Very explicit content that might violate the policy." + + +def notify_moderator(content: Content, approval_id: str): """Notify human moderator about content requiring review.""" print("\n🔍 CONTENT MODERATION REQUIRED 🔍") - print(f"Content: {content}") + print(f"Content: {request.request}") print(f"Awaiting human decision...") print("\nTo approve:") print( @@ -215,3 +168,30 @@ def notify_moderator(content: str, approval_id: str): print( f"curl http://localhost:8080/restate/awakeables/{approval_id}/resolve --json '\"rejected\"'" ) + + +class WeatherRequest(BaseModel): + city: str + + +async def fetch_weather(req: WeatherRequest) -> dict: + # This is a simulated failure to demo Durable Execution retries. + try: + resp = httpx.get( + f"https://wttr.in/{httpx.URL(req.city)}?format=j1", timeout=10.0 + ) + resp.raise_for_status() + + if resp.text.startswith("Unknown location"): + raise TerminalError( + f"Unknown location: {req.city}. Please provide a valid city name." + ) + + return resp.json() + except httpx.HTTPStatusError as e: + if e.response.status_code == 404: + raise TerminalError( + f"City not found: {req.city}. Please provide a valid city name." + ) from e + else: + raise Exception(f"HTTP error occurred: {e}") from e diff --git a/patterns/pyproject.toml b/patterns/pyproject.toml index 12e706d..76842cb 100644 --- a/patterns/pyproject.toml +++ b/patterns/pyproject.toml @@ -9,8 +9,7 @@ dependencies = [ "hypercorn", "restate_sdk[serde]>=0.10.1", "pydantic>=2.11.9", - "openai", - "anthropic", + "litellm" ] [dependency-groups] diff --git a/patterns/uv.lock b/patterns/uv.lock index 6d0c8ec..41d4b82 100644 --- a/patterns/uv.lock +++ b/patterns/uv.lock @@ -3,30 +3,85 @@ revision = 1 requires-python = ">=3.12" [[package]] -name = "annotated-types" -version = "0.7.0" +name = "aiohappyeyeballs" +version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, ] [[package]] -name = "anthropic" -version = "0.54.0" +name = "aiohttp" +version = "3.12.15" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/28/80cb9bb6e7ce77d404145b51da4257455805c17f0a6be528ff3286e3882f/anthropic-0.54.0.tar.gz", hash = "sha256:5e6f997d97ce8e70eac603c3ec2e7f23addeff953fbbb76b19430562bb6ba815", size = 312376 } +sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/b9/6ffb48e82c5e97b03cecee872d134a6b6666c2767b2d32ed709f3a60a8fe/anthropic-0.54.0-py3-none-any.whl", hash = "sha256:c1062a0a905daeec17ca9c06c401e4b3f24cb0495841d29d752568a1d4018d56", size = 288774 }, + { url = "https://files.pythonhosted.org/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333 }, + { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948 }, + { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787 }, + { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590 }, + { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241 }, + { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335 }, + { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491 }, + { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929 }, + { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733 }, + { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790 }, + { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245 }, + { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899 }, + { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459 }, + { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434 }, + { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045 }, + { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591 }, + { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266 }, + { url = "https://files.pythonhosted.org/packages/f2/33/918091abcf102e39d15aba2476ad9e7bd35ddb190dcdd43a854000d3da0d/aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315", size = 696741 }, + { url = "https://files.pythonhosted.org/packages/b5/2a/7495a81e39a998e400f3ecdd44a62107254803d1681d9189be5c2e4530cd/aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd", size = 474407 }, + { url = "https://files.pythonhosted.org/packages/49/fc/a9576ab4be2dcbd0f73ee8675d16c707cfc12d5ee80ccf4015ba543480c9/aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4", size = 466703 }, + { url = "https://files.pythonhosted.org/packages/09/2f/d4bcc8448cf536b2b54eed48f19682031ad182faa3a3fee54ebe5b156387/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7", size = 1705532 }, + { url = "https://files.pythonhosted.org/packages/f1/f3/59406396083f8b489261e3c011aa8aee9df360a96ac8fa5c2e7e1b8f0466/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d", size = 1686794 }, + { url = "https://files.pythonhosted.org/packages/dc/71/164d194993a8d114ee5656c3b7ae9c12ceee7040d076bf7b32fb98a8c5c6/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b", size = 1738865 }, + { url = "https://files.pythonhosted.org/packages/1c/00/d198461b699188a93ead39cb458554d9f0f69879b95078dce416d3209b54/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d", size = 1788238 }, + { url = "https://files.pythonhosted.org/packages/85/b8/9e7175e1fa0ac8e56baa83bf3c214823ce250d0028955dfb23f43d5e61fd/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d", size = 1710566 }, + { url = "https://files.pythonhosted.org/packages/59/e4/16a8eac9df39b48ae102ec030fa9f726d3570732e46ba0c592aeeb507b93/aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645", size = 1624270 }, + { url = "https://files.pythonhosted.org/packages/1f/f8/cd84dee7b6ace0740908fd0af170f9fab50c2a41ccbc3806aabcb1050141/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461", size = 1677294 }, + { url = "https://files.pythonhosted.org/packages/ce/42/d0f1f85e50d401eccd12bf85c46ba84f947a84839c8a1c2c5f6e8ab1eb50/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9", size = 1708958 }, + { url = "https://files.pythonhosted.org/packages/d5/6b/f6fa6c5790fb602538483aa5a1b86fcbad66244997e5230d88f9412ef24c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d", size = 1651553 }, + { url = "https://files.pythonhosted.org/packages/04/36/a6d36ad545fa12e61d11d1932eef273928b0495e6a576eb2af04297fdd3c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693", size = 1727688 }, + { url = "https://files.pythonhosted.org/packages/aa/c8/f195e5e06608a97a4e52c5d41c7927301bf757a8e8bb5bbf8cef6c314961/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64", size = 1761157 }, + { url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050 }, + { url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647 }, + { url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067 }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -43,6 +98,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, ] +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + [[package]] name = "certifi" version = "2025.4.26" @@ -52,6 +116,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655 }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223 }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366 }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104 }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830 }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854 }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670 }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501 }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173 }, + { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822 }, + { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543 }, + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326 }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008 }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196 }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819 }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350 }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644 }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468 }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187 }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699 }, + { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580 }, + { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366 }, + { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342 }, + { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995 }, + { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640 }, + { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636 }, + { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939 }, + { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580 }, + { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870 }, + { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797 }, + { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 }, + { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 }, + { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175 }, +] + +[[package]] +name = "click" +version = "8.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295 }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -79,6 +197,114 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] +[[package]] +name = "fastuuid" +version = "0.13.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/80/3c16a1edad2e6cd82fbd15ac998cc1b881f478bf1f80ca717d941c441874/fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1", size = 18255 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/36/434f137c5970cac19e57834e1f7680e85301619d49891618c00666700c61/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a", size = 494638 }, + { url = "https://files.pythonhosted.org/packages/ca/3c/083de2ac007b2b305523b9c006dba5051e5afd87a626ef1a39f76e2c6b82/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7", size = 253138 }, + { url = "https://files.pythonhosted.org/packages/73/5e/630cffa1c8775db526e39e9e4c5c7db0c27be0786bb21ba82c912ae19f63/fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202", size = 244521 }, + { url = "https://files.pythonhosted.org/packages/4d/51/55d78705f4fbdadf88fb40f382f508d6c7a4941ceddd7825fafebb4cc778/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e", size = 271557 }, + { url = "https://files.pythonhosted.org/packages/6a/2b/1b89e90a8635e5587ccdbbeb169c590672ce7637880f2c047482a0359950/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854", size = 272334 }, + { url = "https://files.pythonhosted.org/packages/0c/06/4c8207894eeb30414999e5c3f66ac039bc4003437eb4060d8a1bceb4cc6f/fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86", size = 290594 }, + { url = "https://files.pythonhosted.org/packages/50/69/96d221931a31d77a47cc2487bdfacfb3091edfc2e7a04b1795df1aec05df/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952", size = 452835 }, + { url = "https://files.pythonhosted.org/packages/25/ef/bf045f0a47dcec96247497ef3f7a31d86ebc074330e2dccc34b8dbc0468a/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9", size = 468225 }, + { url = "https://files.pythonhosted.org/packages/30/46/4817ab5a3778927155a4bde92540d4c4fa996161ec8b8e080c8928b0984e/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3", size = 444907 }, + { url = "https://files.pythonhosted.org/packages/80/27/ab284117ce4dc9b356a7196bdbf220510285f201d27f1f078592cdc8187b/fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85", size = 145415 }, + { url = "https://files.pythonhosted.org/packages/f4/0c/f970a4222773b248931819f8940800b760283216ca3dda173ed027e94bdd/fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a", size = 150840 }, + { url = "https://files.pythonhosted.org/packages/4f/62/74fc53f6e04a4dc5b36c34e4e679f85a4c14eec800dcdb0f2c14b5442217/fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ea17dfd35e0e91920a35d91e65e5f9c9d1985db55ac4ff2f1667a0f61189cefa", size = 494678 }, + { url = "https://files.pythonhosted.org/packages/09/ba/f28b9b7045738a8bfccfb9cd6aff4b91fce2669e6b383a48b0694ee9b3ff/fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:be6ad91e5fefbcc2a4b478858a2715e386d405834ea3ae337c3b6b95cc0e47d6", size = 253162 }, + { url = "https://files.pythonhosted.org/packages/b1/18/13fac89cb4c9f0cd7e81a9154a77ecebcc95d2b03477aa91d4d50f7227ee/fastuuid-0.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ea6df13a306aab3e0439d58c312ff1e6f4f07f09f667579679239b4a6121f64a", size = 244546 }, + { url = "https://files.pythonhosted.org/packages/04/bf/9691167804d59411cc4269841df949f6dd5e76452ab10dcfcd1dbe04c5bc/fastuuid-0.13.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2354c1996d3cf12dc2ba3752e2c4d6edc46e1a38c63893146777b1939f3062d4", size = 271528 }, + { url = "https://files.pythonhosted.org/packages/a9/b5/7a75a03d1c7aa0b6d573032fcca39391f0aef7f2caabeeb45a672bc0bd3c/fastuuid-0.13.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6cf9b7469fc26d1f9b1c43ac4b192e219e85b88fdf81d71aa755a6c08c8a817", size = 272292 }, + { url = "https://files.pythonhosted.org/packages/c0/db/fa0f16cbf76e6880599533af4ef01bb586949c5320612e9d884eff13e603/fastuuid-0.13.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92ba539170097b9047551375f1ca09d8d2b4aefcc79eeae3e1c43fe49b42072e", size = 290466 }, + { url = "https://files.pythonhosted.org/packages/1e/02/6b8c45bfbc8500994dd94edba7f59555f9683c4d8c9a164ae1d25d03c7c7/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:dbb81d05617bc2970765c1ad82db7e8716f6a2b7a361a14b83de5b9240ade448", size = 452838 }, + { url = "https://files.pythonhosted.org/packages/27/12/85d95a84f265b888e8eb9f9e2b5aaf331e8be60c0a7060146364b3544b6a/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:d973bd6bf9d754d3cca874714ac0a6b22a47f239fb3d3c8687569db05aac3471", size = 468149 }, + { url = "https://files.pythonhosted.org/packages/ad/da/dd9a137e9ea707e883c92470113a432233482ec9ad3e9b99c4defc4904e6/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e725ceef79486423f05ee657634d4b4c1ca5fb2c8a94e0708f5d6356a83f2a83", size = 444933 }, + { url = "https://files.pythonhosted.org/packages/12/f4/ab363d7f4ac3989691e2dc5ae2d8391cfb0b4169e52ef7fa0ac363e936f0/fastuuid-0.13.5-cp313-cp313-win32.whl", hash = "sha256:a1c430a332ead0b2674f1ef71b17f43b8139ec5a4201182766a21f131a31e021", size = 145462 }, + { url = "https://files.pythonhosted.org/packages/aa/8a/52eb77d9c294a54caa0d2d8cc9f906207aa6d916a22de963687ab6db8b86/fastuuid-0.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:241fdd362fd96e6b337db62a65dd7cb3dfac20adf854573247a47510e192db6f", size = 150923 }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988 }, +] + +[[package]] +name = "frozenlist" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424 }, + { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952 }, + { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688 }, + { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084 }, + { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524 }, + { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493 }, + { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116 }, + { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557 }, + { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820 }, + { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542 }, + { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350 }, + { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093 }, + { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482 }, + { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590 }, + { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785 }, + { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487 }, + { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874 }, + { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791 }, + { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165 }, + { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881 }, + { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409 }, + { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132 }, + { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638 }, + { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539 }, + { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646 }, + { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233 }, + { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280 }, + { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717 }, + { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644 }, + { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879 }, + { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502 }, + { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169 }, + { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219 }, + { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880 }, + { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498 }, + { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296 }, + { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103 }, + { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869 }, + { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467 }, + { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028 }, + { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294 }, + { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898 }, + { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465 }, + { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385 }, + { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771 }, + { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206 }, + { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620 }, + { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059 }, + { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516 }, + { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106 }, +] + +[[package]] +name = "fsspec" +version = "2025.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289 }, +] + [[package]] name = "h11" version = "0.16.0" @@ -101,6 +327,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, ] +[[package]] +name = "hf-xet" +version = "1.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97", size = 487910 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d", size = 2761466 }, + { url = "https://files.pythonhosted.org/packages/31/f9/6215f948ac8f17566ee27af6430ea72045e0418ce757260248b483f4183b/hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b", size = 2623807 }, + { url = "https://files.pythonhosted.org/packages/15/07/86397573efefff941e100367bbda0b21496ffcdb34db7ab51912994c32a2/hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435", size = 3186960 }, + { url = "https://files.pythonhosted.org/packages/01/a7/0b2e242b918cc30e1f91980f3c4b026ff2eedaf1e2ad96933bca164b2869/hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c", size = 3087167 }, + { url = "https://files.pythonhosted.org/packages/4a/25/3e32ab61cc7145b11eee9d745988e2f0f4fafda81b25980eebf97d8cff15/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06", size = 3248612 }, + { url = "https://files.pythonhosted.org/packages/2c/3d/ab7109e607ed321afaa690f557a9ada6d6d164ec852fd6bf9979665dc3d6/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f", size = 3353360 }, + { url = "https://files.pythonhosted.org/packages/ee/0e/471f0a21db36e71a2f1752767ad77e92d8cde24e974e03d662931b1305ec/hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045", size = 2804691 }, +] + [[package]] name = "hpack" version = "4.1.0" @@ -138,6 +379,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] +[[package]] +name = "huggingface-hub" +version = "0.35.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/7e/a0a97de7c73671863ca6b3f61fa12518caf35db37825e43d63a70956738c/huggingface_hub-0.35.3.tar.gz", hash = "sha256:350932eaa5cc6a4747efae85126ee220e4ef1b54e29d31c3b45c5612ddf0b32a", size = 461798 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/a0/651f93d154cb72323358bf2bbae3e642bdb5d2f1bfc874d096f7cb159fa0/huggingface_hub-0.35.3-py3-none-any.whl", hash = "sha256:0e3a01829c19d86d03793e4577816fe3bdfc1602ac62c7fb220d593d351224ba", size = 564262 }, +] + [[package]] name = "hypercorn" version = "0.17.3" @@ -171,6 +431,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + [[package]] name = "jiter" version = "0.10.0" @@ -219,6 +503,182 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, ] +[[package]] +name = "jsonschema" +version = "4.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, +] + +[[package]] +name = "litellm" +version = "1.77.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "fastuuid" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/a3/85fc92d998ec9645c9fac108618681ef411ca4b338cc7544d6b3aad57699/litellm-1.77.5.tar.gz", hash = "sha256:8e8a83b49c4a6ae044b1a1c01adfbdef72b0031b86f1463dd743e267fa1d7b99", size = 10351819 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/4c/89553f7e375ef39497d86f2266a0cdb37371a07e9e0aa8949f33c15a4198/litellm-1.77.5-py3-none-any.whl", hash = "sha256:07f53964c08d555621d4376cc42330458301ae889bfb6303155dcabc51095fbf", size = 9165458 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + +[[package]] +name = "multidict" +version = "6.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516 }, + { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394 }, + { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591 }, + { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215 }, + { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299 }, + { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357 }, + { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369 }, + { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341 }, + { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100 }, + { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584 }, + { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018 }, + { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477 }, + { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575 }, + { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649 }, + { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505 }, + { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888 }, + { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072 }, + { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222 }, + { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848 }, + { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060 }, + { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269 }, + { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158 }, + { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076 }, + { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694 }, + { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350 }, + { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250 }, + { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900 }, + { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355 }, + { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061 }, + { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675 }, + { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247 }, + { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960 }, + { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078 }, + { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708 }, + { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912 }, + { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076 }, + { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812 }, + { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313 }, + { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777 }, + { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321 }, + { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954 }, + { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612 }, + { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528 }, + { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329 }, + { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928 }, + { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228 }, + { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869 }, + { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446 }, + { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299 }, + { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926 }, + { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383 }, + { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775 }, + { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100 }, + { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501 }, + { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313 }, +] + [[package]] name = "mypy" version = "1.18.2" @@ -262,7 +722,7 @@ wheels = [ [[package]] name = "openai" -version = "1.86.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -274,9 +734,18 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/7a/9ad4a61f1502f0e59d8c27fb629e28a63259a44d8d31cd2314e1534a2d9f/openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b", size = 468272 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/dd/4d4d46a06943e37c95b6e388237e1e38d1e9aab264ff070f86345d60b7a4/openai-2.1.0.tar.gz", hash = "sha256:47f3463a5047340a989b4c0cd5378054acfca966ff61a96553b22f098e3270a2", size = 572998 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/83/88f64fc8f037885efa8a629d1215f5bc1f037453bab4d4f823b5533319eb/openai-2.1.0-py3-none-any.whl", hash = "sha256:33172e8c06a4576144ba4137a493807a9ca427421dcabc54ad3aa656daf757d3", size = 964939 }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/c1/dfb16b3432810fc9758564f9d1a4dbce6b93b7fb763ba57530c7fc48316d/openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349", size = 730296 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] @@ -293,9 +762,8 @@ name = "patterns" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "anthropic" }, { name = "hypercorn" }, - { name = "openai" }, + { name = "litellm" }, { name = "pydantic" }, { name = "restate-sdk", extra = ["serde"] }, ] @@ -307,9 +775,8 @@ dev = [ [package.metadata] requires-dist = [ - { name = "anthropic" }, { name = "hypercorn" }, - { name = "openai" }, + { name = "litellm" }, { name = "pydantic", specifier = ">=2.11.9" }, { name = "restate-sdk", extras = ["serde"], specifier = ">=0.10.1" }, ] @@ -326,6 +793,63 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946 }, ] +[[package]] +name = "propcache" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674 }, + { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570 }, + { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094 }, + { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958 }, + { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894 }, + { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672 }, + { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395 }, + { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510 }, + { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949 }, + { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258 }, + { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036 }, + { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684 }, + { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562 }, + { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142 }, + { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711 }, + { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479 }, + { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286 }, + { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425 }, + { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846 }, + { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871 }, + { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720 }, + { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203 }, + { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365 }, + { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016 }, + { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596 }, + { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977 }, + { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220 }, + { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642 }, + { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789 }, + { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880 }, + { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220 }, + { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560 }, + { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701 }, + { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934 }, + { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316 }, + { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619 }, + { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896 }, + { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111 }, + { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334 }, + { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026 }, + { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724 }, + { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868 }, + { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322 }, + { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778 }, + { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175 }, + { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857 }, + { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663 }, +] + [[package]] name = "pydantic" version = "2.11.9" @@ -383,6 +907,168 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, ] +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "regex" +version = "2025.9.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/d3/eaa0d28aba6ad1827ad1e716d9a93e1ba963ada61887498297d3da715133/regex-2025.9.18.tar.gz", hash = "sha256:c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4", size = 400917 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/99/05859d87a66ae7098222d65748f11ef7f2dff51bfd7482a4e2256c90d72b/regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e", size = 486335 }, + { url = "https://files.pythonhosted.org/packages/97/7e/d43d4e8b978890932cf7b0957fce58c5b08c66f32698f695b0c2c24a48bf/regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a", size = 289720 }, + { url = "https://files.pythonhosted.org/packages/bb/3b/ff80886089eb5dcf7e0d2040d9aaed539e25a94300403814bb24cc775058/regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab", size = 287257 }, + { url = "https://files.pythonhosted.org/packages/ee/66/243edf49dd8720cba8d5245dd4d6adcb03a1defab7238598c0c97cf549b8/regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5", size = 797463 }, + { url = "https://files.pythonhosted.org/packages/df/71/c9d25a1142c70432e68bb03211d4a82299cd1c1fbc41db9409a394374ef5/regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742", size = 862670 }, + { url = "https://files.pythonhosted.org/packages/f8/8f/329b1efc3a64375a294e3a92d43372bf1a351aa418e83c21f2f01cf6ec41/regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425", size = 910881 }, + { url = "https://files.pythonhosted.org/packages/35/9e/a91b50332a9750519320ed30ec378b74c996f6befe282cfa6bb6cea7e9fd/regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352", size = 802011 }, + { url = "https://files.pythonhosted.org/packages/a4/1d/6be3b8d7856b6e0d7ee7f942f437d0a76e0d5622983abbb6d21e21ab9a17/regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d", size = 786668 }, + { url = "https://files.pythonhosted.org/packages/cb/ce/4a60e53df58bd157c5156a1736d3636f9910bdcc271d067b32b7fcd0c3a8/regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56", size = 856578 }, + { url = "https://files.pythonhosted.org/packages/86/e8/162c91bfe7217253afccde112868afb239f94703de6580fb235058d506a6/regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e", size = 849017 }, + { url = "https://files.pythonhosted.org/packages/35/34/42b165bc45289646ea0959a1bc7531733e90b47c56a72067adfe6b3251f6/regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282", size = 788150 }, + { url = "https://files.pythonhosted.org/packages/79/5d/cdd13b1f3c53afa7191593a7ad2ee24092a5a46417725ffff7f64be8342d/regex-2025.9.18-cp312-cp312-win32.whl", hash = "sha256:e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459", size = 264536 }, + { url = "https://files.pythonhosted.org/packages/e0/f5/4a7770c9a522e7d2dc1fa3ffc83ab2ab33b0b22b447e62cffef186805302/regex-2025.9.18-cp312-cp312-win_amd64.whl", hash = "sha256:3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77", size = 275501 }, + { url = "https://files.pythonhosted.org/packages/df/05/9ce3e110e70d225ecbed455b966003a3afda5e58e8aec2964042363a18f4/regex-2025.9.18-cp312-cp312-win_arm64.whl", hash = "sha256:032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5", size = 268601 }, + { url = "https://files.pythonhosted.org/packages/d2/c7/5c48206a60ce33711cf7dcaeaed10dd737733a3569dc7e1dce324dd48f30/regex-2025.9.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a40f929cd907c7e8ac7566ac76225a77701a6221bca937bdb70d56cb61f57b2", size = 485955 }, + { url = "https://files.pythonhosted.org/packages/e9/be/74fc6bb19a3c491ec1ace943e622b5a8539068771e8705e469b2da2306a7/regex-2025.9.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c90471671c2cdf914e58b6af62420ea9ecd06d1554d7474d50133ff26ae88feb", size = 289583 }, + { url = "https://files.pythonhosted.org/packages/25/c4/9ceaa433cb5dc515765560f22a19578b95b92ff12526e5a259321c4fc1a0/regex-2025.9.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a351aff9e07a2dabb5022ead6380cff17a4f10e4feb15f9100ee56c4d6d06af", size = 287000 }, + { url = "https://files.pythonhosted.org/packages/7d/e6/68bc9393cb4dc68018456568c048ac035854b042bc7c33cb9b99b0680afa/regex-2025.9.18-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc4b8e9d16e20ddfe16430c23468a8707ccad3365b06d4536142e71823f3ca29", size = 797535 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/ebae9032d34b78ecfe9bd4b5e6575b55351dc8513485bb92326613732b8c/regex-2025.9.18-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b8cdbddf2db1c5e80338ba2daa3cfa3dec73a46fff2a7dda087c8efbf12d62f", size = 862603 }, + { url = "https://files.pythonhosted.org/packages/3b/74/12332c54b3882557a4bcd2b99f8be581f5c6a43cf1660a85b460dd8ff468/regex-2025.9.18-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a276937d9d75085b2c91fb48244349c6954f05ee97bba0963ce24a9d915b8b68", size = 910829 }, + { url = "https://files.pythonhosted.org/packages/86/70/ba42d5ed606ee275f2465bfc0e2208755b06cdabd0f4c7c4b614d51b57ab/regex-2025.9.18-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92a8e375ccdc1256401c90e9dc02b8642894443d549ff5e25e36d7cf8a80c783", size = 802059 }, + { url = "https://files.pythonhosted.org/packages/da/c5/fcb017e56396a7f2f8357412638d7e2963440b131a3ca549be25774b3641/regex-2025.9.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dc6893b1f502d73037cf807a321cdc9be29ef3d6219f7970f842475873712ac", size = 786781 }, + { url = "https://files.pythonhosted.org/packages/c6/ee/21c4278b973f630adfb3bcb23d09d83625f3ab1ca6e40ebdffe69901c7a1/regex-2025.9.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a61e85bfc63d232ac14b015af1261f826260c8deb19401c0597dbb87a864361e", size = 856578 }, + { url = "https://files.pythonhosted.org/packages/87/0b/de51550dc7274324435c8f1539373ac63019b0525ad720132866fff4a16a/regex-2025.9.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ef86a9ebc53f379d921fb9a7e42b92059ad3ee800fcd9e0fe6181090e9f6c23", size = 849119 }, + { url = "https://files.pythonhosted.org/packages/60/52/383d3044fc5154d9ffe4321696ee5b2ee4833a28c29b137c22c33f41885b/regex-2025.9.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d3bc882119764ba3a119fbf2bd4f1b47bc56c1da5d42df4ed54ae1e8e66fdf8f", size = 788219 }, + { url = "https://files.pythonhosted.org/packages/20/bd/2614fc302671b7359972ea212f0e3a92df4414aaeacab054a8ce80a86073/regex-2025.9.18-cp313-cp313-win32.whl", hash = "sha256:3810a65675845c3bdfa58c3c7d88624356dd6ee2fc186628295e0969005f928d", size = 264517 }, + { url = "https://files.pythonhosted.org/packages/07/0f/ab5c1581e6563a7bffdc1974fb2d25f05689b88e2d416525271f232b1946/regex-2025.9.18-cp313-cp313-win_amd64.whl", hash = "sha256:16eaf74b3c4180ede88f620f299e474913ab6924d5c4b89b3833bc2345d83b3d", size = 275481 }, + { url = "https://files.pythonhosted.org/packages/49/22/ee47672bc7958f8c5667a587c2600a4fba8b6bab6e86bd6d3e2b5f7cac42/regex-2025.9.18-cp313-cp313-win_arm64.whl", hash = "sha256:4dc98ba7dd66bd1261927a9f49bd5ee2bcb3660f7962f1ec02617280fc00f5eb", size = 268598 }, + { url = "https://files.pythonhosted.org/packages/e8/83/6887e16a187c6226cb85d8301e47d3b73ecc4505a3a13d8da2096b44fd76/regex-2025.9.18-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fe5d50572bc885a0a799410a717c42b1a6b50e2f45872e2b40f4f288f9bce8a2", size = 489765 }, + { url = "https://files.pythonhosted.org/packages/51/c5/e2f7325301ea2916ff301c8d963ba66b1b2c1b06694191df80a9c4fea5d0/regex-2025.9.18-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b9d9a2d6cda6621551ca8cf7a06f103adf72831153f3c0d982386110870c4d3", size = 291228 }, + { url = "https://files.pythonhosted.org/packages/91/60/7d229d2bc6961289e864a3a3cfebf7d0d250e2e65323a8952cbb7e22d824/regex-2025.9.18-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:13202e4c4ac0ef9a317fff817674b293c8f7e8c68d3190377d8d8b749f566e12", size = 289270 }, + { url = "https://files.pythonhosted.org/packages/3c/d7/b4f06868ee2958ff6430df89857fbf3d43014bbf35538b6ec96c2704e15d/regex-2025.9.18-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874ff523b0fecffb090f80ae53dc93538f8db954c8bb5505f05b7787ab3402a0", size = 806326 }, + { url = "https://files.pythonhosted.org/packages/d6/e4/bca99034a8f1b9b62ccf337402a8e5b959dd5ba0e5e5b2ead70273df3277/regex-2025.9.18-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d13ab0490128f2bb45d596f754148cd750411afc97e813e4b3a61cf278a23bb6", size = 871556 }, + { url = "https://files.pythonhosted.org/packages/6d/df/e06ffaf078a162f6dd6b101a5ea9b44696dca860a48136b3ae4a9caf25e2/regex-2025.9.18-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:05440bc172bc4b4b37fb9667e796597419404dbba62e171e1f826d7d2a9ebcef", size = 913817 }, + { url = "https://files.pythonhosted.org/packages/9e/05/25b05480b63292fd8e84800b1648e160ca778127b8d2367a0a258fa2e225/regex-2025.9.18-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5514b8e4031fdfaa3d27e92c75719cbe7f379e28cacd939807289bce76d0e35a", size = 811055 }, + { url = "https://files.pythonhosted.org/packages/70/97/7bc7574655eb651ba3a916ed4b1be6798ae97af30104f655d8efd0cab24b/regex-2025.9.18-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:65d3c38c39efce73e0d9dc019697b39903ba25b1ad45ebbd730d2cf32741f40d", size = 794534 }, + { url = "https://files.pythonhosted.org/packages/b4/c2/d5da49166a52dda879855ecdba0117f073583db2b39bb47ce9a3378a8e9e/regex-2025.9.18-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ae77e447ebc144d5a26d50055c6ddba1d6ad4a865a560ec7200b8b06bc529368", size = 866684 }, + { url = "https://files.pythonhosted.org/packages/bd/2d/0a5c4e6ec417de56b89ff4418ecc72f7e3feca806824c75ad0bbdae0516b/regex-2025.9.18-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3ef8cf53dc8df49d7e28a356cf824e3623764e9833348b655cfed4524ab8a90", size = 853282 }, + { url = "https://files.pythonhosted.org/packages/f4/8e/d656af63e31a86572ec829665d6fa06eae7e144771e0330650a8bb865635/regex-2025.9.18-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9feb29817df349c976da9a0debf775c5c33fc1c8ad7b9f025825da99374770b7", size = 797830 }, + { url = "https://files.pythonhosted.org/packages/db/ce/06edc89df8f7b83ffd321b6071be4c54dc7332c0f77860edc40ce57d757b/regex-2025.9.18-cp313-cp313t-win32.whl", hash = "sha256:168be0d2f9b9d13076940b1ed774f98595b4e3c7fc54584bba81b3cc4181742e", size = 267281 }, + { url = "https://files.pythonhosted.org/packages/83/9a/2b5d9c8b307a451fd17068719d971d3634ca29864b89ed5c18e499446d4a/regex-2025.9.18-cp313-cp313t-win_amd64.whl", hash = "sha256:d59ecf3bb549e491c8104fea7313f3563c7b048e01287db0a90485734a70a730", size = 278724 }, + { url = "https://files.pythonhosted.org/packages/3d/70/177d31e8089a278a764f8ec9a3faac8d14a312d622a47385d4b43905806f/regex-2025.9.18-cp313-cp313t-win_arm64.whl", hash = "sha256:dbef80defe9fb21310948a2595420b36c6d641d9bea4c991175829b2cc4bc06a", size = 269771 }, + { url = "https://files.pythonhosted.org/packages/44/b7/3b4663aa3b4af16819f2ab6a78c4111c7e9b066725d8107753c2257448a5/regex-2025.9.18-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c6db75b51acf277997f3adcd0ad89045d856190d13359f15ab5dda21581d9129", size = 486130 }, + { url = "https://files.pythonhosted.org/packages/80/5b/4533f5d7ac9c6a02a4725fe8883de2aebc713e67e842c04cf02626afb747/regex-2025.9.18-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8f9698b6f6895d6db810e0bda5364f9ceb9e5b11328700a90cae573574f61eea", size = 289539 }, + { url = "https://files.pythonhosted.org/packages/b8/8d/5ab6797c2750985f79e9995fad3254caa4520846580f266ae3b56d1cae58/regex-2025.9.18-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29cd86aa7cb13a37d0f0d7c21d8d949fe402ffa0ea697e635afedd97ab4b69f1", size = 287233 }, + { url = "https://files.pythonhosted.org/packages/cb/1e/95afcb02ba8d3a64e6ffeb801718ce73471ad6440c55d993f65a4a5e7a92/regex-2025.9.18-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c9f285a071ee55cd9583ba24dde006e53e17780bb309baa8e4289cd472bcc47", size = 797876 }, + { url = "https://files.pythonhosted.org/packages/c8/fb/720b1f49cec1f3b5a9fea5b34cd22b88b5ebccc8c1b5de9cc6f65eed165a/regex-2025.9.18-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5adf266f730431e3be9021d3e5b8d5ee65e563fec2883ea8093944d21863b379", size = 863385 }, + { url = "https://files.pythonhosted.org/packages/a9/ca/e0d07ecf701e1616f015a720dc13b84c582024cbfbb3fc5394ae204adbd7/regex-2025.9.18-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1137cabc0f38807de79e28d3f6e3e3f2cc8cfb26bead754d02e6d1de5f679203", size = 910220 }, + { url = "https://files.pythonhosted.org/packages/b6/45/bba86413b910b708eca705a5af62163d5d396d5f647ed9485580c7025209/regex-2025.9.18-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cc9e5525cada99699ca9223cce2d52e88c52a3d2a0e842bd53de5497c604164", size = 801827 }, + { url = "https://files.pythonhosted.org/packages/b8/a6/740fbd9fcac31a1305a8eed30b44bf0f7f1e042342be0a4722c0365ecfca/regex-2025.9.18-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bbb9246568f72dce29bcd433517c2be22c7791784b223a810225af3b50d1aafb", size = 786843 }, + { url = "https://files.pythonhosted.org/packages/80/a7/0579e8560682645906da640c9055506465d809cb0f5415d9976f417209a6/regex-2025.9.18-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6a52219a93dd3d92c675383efff6ae18c982e2d7651c792b1e6d121055808743", size = 857430 }, + { url = "https://files.pythonhosted.org/packages/8d/9b/4dc96b6c17b38900cc9fee254fc9271d0dde044e82c78c0811b58754fde5/regex-2025.9.18-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:ae9b3840c5bd456780e3ddf2f737ab55a79b790f6409182012718a35c6d43282", size = 848612 }, + { url = "https://files.pythonhosted.org/packages/b3/6a/6f659f99bebb1775e5ac81a3fb837b85897c1a4ef5acffd0ff8ffe7e67fb/regex-2025.9.18-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d488c236ac497c46a5ac2005a952c1a0e22a07be9f10c3e735bc7d1209a34773", size = 787967 }, + { url = "https://files.pythonhosted.org/packages/61/35/9e35665f097c07cf384a6b90a1ac11b0b1693084a0b7a675b06f760496c6/regex-2025.9.18-cp314-cp314-win32.whl", hash = "sha256:0c3506682ea19beefe627a38872d8da65cc01ffa25ed3f2e422dffa1474f0788", size = 269847 }, + { url = "https://files.pythonhosted.org/packages/af/64/27594dbe0f1590b82de2821ebfe9a359b44dcb9b65524876cd12fabc447b/regex-2025.9.18-cp314-cp314-win_amd64.whl", hash = "sha256:57929d0f92bebb2d1a83af372cd0ffba2263f13f376e19b1e4fa32aec4efddc3", size = 278755 }, + { url = "https://files.pythonhosted.org/packages/30/a3/0cd8d0d342886bd7d7f252d701b20ae1a3c72dc7f34ef4b2d17790280a09/regex-2025.9.18-cp314-cp314-win_arm64.whl", hash = "sha256:6a4b44df31d34fa51aa5c995d3aa3c999cec4d69b9bd414a8be51984d859f06d", size = 271873 }, + { url = "https://files.pythonhosted.org/packages/99/cb/8a1ab05ecf404e18b54348e293d9b7a60ec2bd7aa59e637020c5eea852e8/regex-2025.9.18-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b176326bcd544b5e9b17d6943f807697c0cb7351f6cfb45bf5637c95ff7e6306", size = 489773 }, + { url = "https://files.pythonhosted.org/packages/93/3b/6543c9b7f7e734d2404fa2863d0d710c907bef99d4598760ed4563d634c3/regex-2025.9.18-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0ffd9e230b826b15b369391bec167baed57c7ce39efc35835448618860995946", size = 291221 }, + { url = "https://files.pythonhosted.org/packages/cd/91/e9fdee6ad6bf708d98c5d17fded423dcb0661795a49cba1b4ffb8358377a/regex-2025.9.18-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec46332c41add73f2b57e2f5b642f991f6b15e50e9f86285e08ffe3a512ac39f", size = 289268 }, + { url = "https://files.pythonhosted.org/packages/94/a6/bc3e8a918abe4741dadeaeb6c508e3a4ea847ff36030d820d89858f96a6c/regex-2025.9.18-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b80fa342ed1ea095168a3f116637bd1030d39c9ff38dc04e54ef7c521e01fc95", size = 806659 }, + { url = "https://files.pythonhosted.org/packages/2b/71/ea62dbeb55d9e6905c7b5a49f75615ea1373afcad95830047e4e310db979/regex-2025.9.18-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4d97071c0ba40f0cf2a93ed76e660654c399a0a04ab7d85472239460f3da84b", size = 871701 }, + { url = "https://files.pythonhosted.org/packages/6a/90/fbe9dedb7dad24a3a4399c0bae64bfa932ec8922a0a9acf7bc88db30b161/regex-2025.9.18-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0ac936537ad87cef9e0e66c5144484206c1354224ee811ab1519a32373e411f3", size = 913742 }, + { url = "https://files.pythonhosted.org/packages/f0/1c/47e4a8c0e73d41eb9eb9fdeba3b1b810110a5139a2526e82fd29c2d9f867/regex-2025.9.18-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dec57f96d4def58c422d212d414efe28218d58537b5445cf0c33afb1b4768571", size = 811117 }, + { url = "https://files.pythonhosted.org/packages/2a/da/435f29fddfd015111523671e36d30af3342e8136a889159b05c1d9110480/regex-2025.9.18-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48317233294648bf7cd068857f248e3a57222259a5304d32c7552e2284a1b2ad", size = 794647 }, + { url = "https://files.pythonhosted.org/packages/23/66/df5e6dcca25c8bc57ce404eebc7342310a0d218db739d7882c9a2b5974a3/regex-2025.9.18-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:274687e62ea3cf54846a9b25fc48a04459de50af30a7bd0b61a9e38015983494", size = 866747 }, + { url = "https://files.pythonhosted.org/packages/82/42/94392b39b531f2e469b2daa40acf454863733b674481fda17462a5ffadac/regex-2025.9.18-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a78722c86a3e7e6aadf9579e3b0ad78d955f2d1f1a8ca4f67d7ca258e8719d4b", size = 853434 }, + { url = "https://files.pythonhosted.org/packages/a8/f8/dcc64c7f7bbe58842a8f89622b50c58c3598fbbf4aad0a488d6df2c699f1/regex-2025.9.18-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:06104cd203cdef3ade989a1c45b6215bf42f8b9dd705ecc220c173233f7cba41", size = 798024 }, + { url = "https://files.pythonhosted.org/packages/20/8d/edf1c5d5aa98f99a692313db813ec487732946784f8f93145e0153d910e5/regex-2025.9.18-cp314-cp314t-win32.whl", hash = "sha256:2e1eddc06eeaffd249c0adb6fafc19e2118e6308c60df9db27919e96b5656096", size = 273029 }, + { url = "https://files.pythonhosted.org/packages/a7/24/02d4e4f88466f17b145f7ea2b2c11af3a942db6222429c2c146accf16054/regex-2025.9.18-cp314-cp314t-win_amd64.whl", hash = "sha256:8620d247fb8c0683ade51217b459cb4a1081c0405a3072235ba43a40d355c09a", size = 282680 }, + { url = "https://files.pythonhosted.org/packages/1f/a3/c64894858aaaa454caa7cc47e2f225b04d3ed08ad649eacf58d45817fad2/regex-2025.9.18-cp314-cp314t-win_arm64.whl", hash = "sha256:b7531a8ef61de2c647cdf68b3229b071e46ec326b3138b2180acb4275f470b01", size = 273034 }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, +] + [[package]] name = "restate-sdk" version = "0.10.2" @@ -413,6 +1099,87 @@ serde = [ { name = "pydantic" }, ] +[[package]] +name = "rpds-py" +version = "0.27.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795 }, + { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121 }, + { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976 }, + { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953 }, + { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915 }, + { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883 }, + { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699 }, + { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713 }, + { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324 }, + { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646 }, + { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137 }, + { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343 }, + { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497 }, + { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790 }, + { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741 }, + { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574 }, + { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051 }, + { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395 }, + { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334 }, + { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691 }, + { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868 }, + { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469 }, + { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125 }, + { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341 }, + { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511 }, + { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736 }, + { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462 }, + { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034 }, + { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392 }, + { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355 }, + { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138 }, + { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247 }, + { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699 }, + { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852 }, + { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582 }, + { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126 }, + { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486 }, + { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832 }, + { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249 }, + { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356 }, + { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300 }, + { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714 }, + { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943 }, + { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472 }, + { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676 }, + { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313 }, + { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080 }, + { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868 }, + { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750 }, + { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688 }, + { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225 }, + { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361 }, + { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493 }, + { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623 }, + { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800 }, + { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943 }, + { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739 }, + { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120 }, + { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944 }, + { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283 }, + { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320 }, + { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760 }, + { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476 }, + { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418 }, + { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771 }, + { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787 }, + { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538 }, + { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512 }, + { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813 }, + { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385 }, + { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097 }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -422,6 +1189,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] +[[package]] +name = "tiktoken" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/86/ad0155a37c4f310935d5ac0b1ccf9bdb635dcb906e0a9a26b616dd55825a/tiktoken-0.11.0.tar.gz", hash = "sha256:3c518641aee1c52247c2b97e74d8d07d780092af79d5911a6ab5e79359d9b06a", size = 37648 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9e/eceddeffc169fc75fe0fd4f38471309f11cb1906f9b8aa39be4f5817df65/tiktoken-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fd9e6b23e860973cf9526544e220b223c60badf5b62e80a33509d6d40e6c8f5d", size = 1055199 }, + { url = "https://files.pythonhosted.org/packages/4f/cf/5f02bfefffdc6b54e5094d2897bc80efd43050e5b09b576fd85936ee54bf/tiktoken-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76d53cee2da71ee2731c9caa747398762bda19d7f92665e882fef229cb0b5b", size = 996655 }, + { url = "https://files.pythonhosted.org/packages/65/8e/c769b45ef379bc360c9978c4f6914c79fd432400a6733a8afc7ed7b0726a/tiktoken-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef72aab3ea240646e642413cb363b73869fed4e604dcfd69eec63dc54d603e8", size = 1128867 }, + { url = "https://files.pythonhosted.org/packages/d5/2d/4d77f6feb9292bfdd23d5813e442b3bba883f42d0ac78ef5fdc56873f756/tiktoken-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f929255c705efec7a28bf515e29dc74220b2f07544a8c81b8d69e8efc4578bd", size = 1183308 }, + { url = "https://files.pythonhosted.org/packages/7a/65/7ff0a65d3bb0fc5a1fb6cc71b03e0f6e71a68c5eea230d1ff1ba3fd6df49/tiktoken-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61f1d15822e4404953d499fd1dcc62817a12ae9fb1e4898033ec8fe3915fdf8e", size = 1244301 }, + { url = "https://files.pythonhosted.org/packages/f5/6e/5b71578799b72e5bdcef206a214c3ce860d999d579a3b56e74a6c8989ee2/tiktoken-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:45927a71ab6643dfd3ef57d515a5db3d199137adf551f66453be098502838b0f", size = 884282 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/a9034bcee638716d9310443818d73c6387a6a96db93cbcb0819b77f5b206/tiktoken-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a5f3f25ffb152ee7fec78e90a5e5ea5b03b4ea240beed03305615847f7a6ace2", size = 1055339 }, + { url = "https://files.pythonhosted.org/packages/f1/91/9922b345f611b4e92581f234e64e9661e1c524875c8eadd513c4b2088472/tiktoken-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dc6e9ad16a2a75b4c4be7208055a1f707c9510541d94d9cc31f7fbdc8db41d8", size = 997080 }, + { url = "https://files.pythonhosted.org/packages/d0/9d/49cd047c71336bc4b4af460ac213ec1c457da67712bde59b892e84f1859f/tiktoken-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a0517634d67a8a48fd4a4ad73930c3022629a85a217d256a6e9b8b47439d1e4", size = 1128501 }, + { url = "https://files.pythonhosted.org/packages/52/d5/a0dcdb40dd2ea357e83cb36258967f0ae96f5dd40c722d6e382ceee6bba9/tiktoken-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fb4effe60574675118b73c6fbfd3b5868e5d7a1f570d6cc0d18724b09ecf318", size = 1182743 }, + { url = "https://files.pythonhosted.org/packages/3b/17/a0fc51aefb66b7b5261ca1314afa83df0106b033f783f9a7bcbe8e741494/tiktoken-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94f984c9831fd32688aef4348803b0905d4ae9c432303087bae370dc1381a2b8", size = 1244057 }, + { url = "https://files.pythonhosted.org/packages/50/79/bcf350609f3a10f09fe4fc207f132085e497fdd3612f3925ab24d86a0ca0/tiktoken-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2177ffda31dec4023356a441793fed82f7af5291120751dee4d696414f54db0c", size = 883901 }, +] + +[[package]] +name = "tokenizers" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318 }, + { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478 }, + { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994 }, + { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141 }, + { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049 }, + { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730 }, + { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560 }, + { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221 }, + { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569 }, + { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599 }, + { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862 }, + { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250 }, + { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003 }, + { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -455,6 +1271,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, ] +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, +] + [[package]] name = "wsproto" version = "1.2.0" @@ -466,3 +1291,77 @@ sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d77642 wheels = [ { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 }, ] + +[[package]] +name = "yarl" +version = "1.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667 }, + { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025 }, + { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709 }, + { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287 }, + { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429 }, + { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429 }, + { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862 }, + { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616 }, + { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954 }, + { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575 }, + { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061 }, + { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142 }, + { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894 }, + { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378 }, + { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069 }, + { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249 }, + { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710 }, + { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811 }, + { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078 }, + { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748 }, + { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595 }, + { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616 }, + { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324 }, + { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676 }, + { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614 }, + { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766 }, + { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615 }, + { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982 }, + { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792 }, + { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049 }, + { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774 }, + { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252 }, + { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198 }, + { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346 }, + { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826 }, + { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217 }, + { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700 }, + { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644 }, + { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452 }, + { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378 }, + { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261 }, + { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987 }, + { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361 }, + { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460 }, + { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486 }, + { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219 }, + { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693 }, + { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803 }, + { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709 }, + { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591 }, + { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003 }, + { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542 }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, +] From f6e6d9d46c4b4b94060bc8afb90958b95e08efcc Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Fri, 3 Oct 2025 22:49:34 +0200 Subject: [PATCH 2/6] Simplify examples --- patterns/__main__.py | 4 +- patterns/app/agent_with_tool.py | 55 ++++++++++++++++++++++++++++ patterns/app/chaining.py | 15 +++++--- patterns/app/chat.py | 7 ++-- patterns/app/evaluator_optimizer.py | 31 ++++++---------- patterns/app/human_in_the_loop.py | 5 +-- patterns/app/orchestrator_workers.py | 52 ++++++++++++++++---------- patterns/app/parallel_agents.py | 4 +- patterns/app/parallel_tools.py | 27 +++++++------- patterns/app/routing_to_agent.py | 15 +++++--- patterns/app/routing_to_tool.py | 4 +- patterns/app/util/util.py | 4 +- 12 files changed, 147 insertions(+), 76 deletions(-) create mode 100644 patterns/app/agent_with_tool.py diff --git a/patterns/__main__.py b/patterns/__main__.py index e716de4..7782cbf 100644 --- a/patterns/__main__.py +++ b/patterns/__main__.py @@ -3,7 +3,8 @@ import restate from app.chaining import call_chaining_svc -from app.parallelization import parallelization_svc +from app.parallel_tools import parallel_tools_agent +from app.parallel_agents import parallelization_svc from app.routing_to_agent import agent_router_service from app.routing_to_tool import tool_router_service from app.orchestrator_workers import orchestrator_svc @@ -21,6 +22,7 @@ services=[ call_chaining_svc, parallelization_svc, + parallel_tools_agent, agent_router_service, tool_router_service, orchestrator_svc, diff --git a/patterns/app/agent_with_tool.py b/patterns/app/agent_with_tool.py new file mode 100644 index 0000000..846a559 --- /dev/null +++ b/patterns/app/agent_with_tool.py @@ -0,0 +1,55 @@ +import litellm +import restate +from restate import Context + +from app.util.util import get_weather, WeatherRequest + +weather_agent = restate.Service("WeatherAgent") + + +@weather_agent.handler() +async def run(ctx: Context, prompt: str) -> str: + """Main agent loop with tool calling""" + messages = [{"role": "user", "content": prompt}] + + while True: + # Call LLM with durable execution + result = await ctx.run_typed( + "llm-call", + litellm.completion, + model = "gpt-4o", + messages=messages, + tools=[{ + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather in a given location", + "parameters": WeatherRequest.model_json_schema(), + }, + }], + ) + response = result.choices[0].message + messages.append(response) + + # No tool calls, return the response + if not response.tool_calls: + return response.content + + # Sequentially call each tool and add the result to messages + for tool_call in response.tool_calls: + if tool_call.function.name == "get_weather": + tool_output = await ctx.run_typed( + "Get weather", + get_weather, + req=WeatherRequest.model_validate_json( + tool_call.function.arguments + ), + ) + messages.append( + { + "role": "tool", + "tool_call_id": tool_call.id, + "content": tool_output, + } + ) + diff --git a/patterns/app/chaining.py b/patterns/app/chaining.py index 6412ee2..a199412 100644 --- a/patterns/app/chaining.py +++ b/patterns/app/chaining.py @@ -14,13 +14,14 @@ call_chaining_svc = restate.Service("CallChainingService") +example_prompt = """Q3 Performance Summary: +Our customer satisfaction score rose to 92 points this quarter. +Revenue grew by 45% compared to last year. +Market share is now at 23% in our primary market. +Customer churn decreased to 5% from 8%.""" class Prompt(BaseModel): - message: str = "Q3 Performance Summary: " - "Our customer satisfaction score rose to 92 points this quarter. " - "Revenue grew by 45% compared to last year. " - "Market share is now at 23% in our primary market. " - "Customer churn decreased to 5% from 8%." + message: str = example_prompt @call_chaining_svc.handler() @@ -45,9 +46,11 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: ) # Step 3: Process the result from Step 2 - return await ctx.run_typed( + result3 = await ctx.run_typed( "Format as table", llm_call, restate.RunOptions(max_attempts=3), prompt=f"Format the sorted data as a markdown table with columns 'Metric Name' and 'Value'. Input: {result2}", ) + + return result3.content diff --git a/patterns/app/chat.py b/patterns/app/chat.py index 43f404e..ac1e4f2 100644 --- a/patterns/app/chat.py +++ b/patterns/app/chat.py @@ -1,4 +1,5 @@ import restate +from litellm.types.utils import Message from pydantic import BaseModel from .util.litellm_call import llm_call @@ -18,7 +19,7 @@ class Prompt(BaseModel): async def message(ctx: restate.ObjectContext, prompt: Prompt) -> str: """A long-lived stateful chat session that allows for ongoing conversation.""" - memory = await ctx.get("memory", type_hint=list[dict[str, str]]) or [] + memory = await ctx.get("memory", type_hint=list[dict]) or [] memory.append({"role": "user", "content": prompt.message}) result = await ctx.run_typed( @@ -28,6 +29,6 @@ async def message(ctx: restate.ObjectContext, prompt: Prompt) -> str: messages=memory, ) - memory.append({"role": "assistant", "content": result}) + memory.append({"role": "user", "content": result.content}) ctx.set("memory", memory) - return result + return result.content diff --git a/patterns/app/evaluator_optimizer.py b/patterns/app/evaluator_optimizer.py index 6f37154..c4e3ddf 100644 --- a/patterns/app/evaluator_optimizer.py +++ b/patterns/app/evaluator_optimizer.py @@ -29,44 +29,37 @@ class Prompt(BaseModel): async def improve_until_good(ctx: restate.Context, prompt: Prompt) -> str: """Iteratively improve a solution until it meets quality standards.""" - attempts: list[str] = [] max_iterations = 5 + solution = "" + attempts = [] for iteration in range(max_iterations): # Generate solution (with context from previous attempts) - context = "" - if attempts: - context = f"\nPrevious attempts that need improvement:\n" + "\n".join( - f"- {a}" for a in attempts[-2:] - ) - - solution = await ctx.run_typed( + solution_response = await ctx.run_typed( f"generate_v{iteration+1}", llm_call, restate.RunOptions(max_attempts=3), system="Create a Python function to solve this task. Eagerly return results for review.", - prompt=f" Previous attempts: {context} - Task: {prompt}" "", + prompt=f" Previous attempts: {attempts} - Task: {prompt}" "", ) + solution = solution_response.content + attempts.append(solution) # Evaluate the solution - evaluation = await ctx.run_typed( + evaluation_response = await ctx.run_typed( f"evaluate_v{iteration+1}", llm_call, restate.RunOptions(max_attempts=3), - prompt=f"""Evaluate this solution on correctness, efficiency, and readability. + system=f"""Evaluate this solution on correctness, efficiency, and readability. Reply with either: 'PASS: [brief reason]' if the solution is correct and very well-implemented - 'IMPROVE: [specific issues to fix]' if it needs work - - Task: {prompt} - Solution: {solution}""", + 'IMPROVE: [specific issues to fix]' if it needs work""", + prompt=f"Task: {prompt} - Solution: {solution}""" ) + evaluation = evaluation_response.content print_evaluation(iteration, solution, evaluation) if evaluation.startswith("PASS"): return solution - # Store failed attempt for context - attempts.append(solution) - - return f"Max iterations reached. Best attempt:\n{solution}" + return f"Max iterations reached. Best attempt:\n {solution}" diff --git a/patterns/app/human_in_the_loop.py b/patterns/app/human_in_the_loop.py index 18e61b6..e7ebc11 100644 --- a/patterns/app/human_in_the_loop.py +++ b/patterns/app/human_in_the_loop.py @@ -15,7 +15,7 @@ @content_moderator_svc.handler() async def moderate(ctx: restate.Context, content: Content) -> str: - """Restate service handler as the durable entry point for content moderation.""" + """Moderate content with human-in-the-loop review""" # Durable step for LLM inference, auto retried & recovered result = await ctx.run_typed( @@ -35,9 +35,8 @@ async def moderate(ctx: restate.Context, content: Content) -> str: } ], ) - result.append(result.model_dump()) - # Otherwise, handle each tool call + # request human review, if ordered by LLM if result.tool_calls and result.tool_calls[0].function.name == "get_human_review": # Create a durable promise (awakeable), approval_id, approval_promise = ctx.awakeable(type_hint=str) diff --git a/patterns/app/orchestrator_workers.py b/patterns/app/orchestrator_workers.py index bc1db80..6f354b2 100644 --- a/patterns/app/orchestrator_workers.py +++ b/patterns/app/orchestrator_workers.py @@ -1,9 +1,12 @@ import restate +from litellm.types.utils import ModelResponse -from pydantic import BaseModel +from pydantic import BaseModel, RootModel +from restate import RunOptions from .util.litellm_call import llm_call -from .util.util import parse_instructions + +import litellm """ LLM Orchestrator-Workers @@ -29,39 +32,50 @@ class Prompt(BaseModel): message: str = example_prompt +class Task(BaseModel): + task_type: str + instruction: str + +class TaskList(BaseModel): + tasks: list[Task] @orchestrator_svc.handler() async def process_text(ctx: restate.Context, prompt: Prompt) -> list[str]: """Orchestrate text analysis breakdown and parallel execution by specialized workers.""" + messages = [ + {"role": "system", + "content": "You are an orchestrator that breaks down text analysis tasks into specialized subtasks for workers."}, + {"role": "user", "content": f"Text to analyze: {prompt.message}"} + ] + # Step 1: Orchestrator analyzes and breaks down the text analysis task - task_breakdown = await ctx.run_typed( + litellm.enable_json_schema_validation = True + response = await ctx.run_typed( "orchestrator_analysis", - llm_call, - restate.RunOptions(max_attempts=3), - system="""You are an orchestrator breaking down text analysis into specific subtasks. - For each task, specify what the worker should focus on: - [task_type]: [specific prompt/instructions for worker]""", - prompt=f"Text to analyze: {prompt}", + litellm.completion, + RunOptions(max_attempts=3, type_hint=ModelResponse), + model="gpt-4o", + messages=messages, + response_format=TaskList ) - - # Parse the task breakdown - worker_instructions = parse_instructions(task_breakdown) + task_list_json = response.choices[0].message.content + task_list = TaskList.model_validate_json(task_list_json) # Step 2: Workers execute their specialized tasks in parallel worker_tasks = [] - for task_type, instruction in worker_instructions.items(): + for task in task_list.tasks: worker_task = ctx.run_typed( - f"worker_{task_type.lower()}", + task.task_type, llm_call, restate.RunOptions(max_attempts=3), - system=f"You are a {task_type} specialist.", - prompt=f"Task: {instruction} - Text to analyze: {prompt}", + system=f"You are a {task.task_type} specialist.", + prompt=f"Task: {task.instruction} - Text to analyze: {prompt}", ) - worker_tasks.append((task_type, worker_task)) + worker_tasks.append({"task_type": task.task_type, "task_promise": worker_task}) # Wait for all workers to complete - await restate.gather(*[task for _, task in worker_tasks]) + await restate.gather(*[task["task_promise"] for task in worker_tasks]) # Collect results - return [f"{task_type} result: {await task}" for task_type, task in worker_tasks] + return [f"{task["task_type"]} result: {await task["task_promise"]}" for task in worker_tasks] diff --git a/patterns/app/parallel_agents.py b/patterns/app/parallel_agents.py index 383a535..e729745 100644 --- a/patterns/app/parallel_agents.py +++ b/patterns/app/parallel_agents.py @@ -14,7 +14,7 @@ Task C ↗ """ -parallelization_svc = restate.Service("ParallelizationService") +parallelization_svc = restate.Service("ParallelAgentsService") # Example input text to analyze example_prompt = ( @@ -59,4 +59,4 @@ async def analyze_text(ctx: restate.Context, prompt: Prompt) -> list[str]: results = await restate.gather(sentiment_task, key_points_task, summary_task) # Gather and collect results - return [await result for result in results] + return [(await result).content for result in results] diff --git a/patterns/app/parallel_tools.py b/patterns/app/parallel_tools.py index 601bd0c..4fb95ee 100644 --- a/patterns/app/parallel_tools.py +++ b/patterns/app/parallel_tools.py @@ -3,15 +3,16 @@ from restate import Context, RunOptions from app.util.litellm_call import llm_call -from app.util.util import fetch_weather, WeatherRequest +from app.util.util import get_weather, WeatherRequest -manual_loop_agent = restate.Service("ParallelToolAgent") +parallel_tools_agent = restate.Service("ParallelToolAgent") -get_weather = { +get_weather_tool= { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", + "parameters": WeatherRequest.model_json_schema(), }, } @@ -20,7 +21,7 @@ class MultiWeatherPrompt(BaseModel): message: str = "What is the weather like in New York, San Francisco, and Boston?" -@manual_loop_agent.handler() +@parallel_tools_agent.handler() async def run(ctx: Context, prompt: MultiWeatherPrompt) -> str: """Main agent loop with tool calling""" messages = [{"role": "user", "content": prompt.message}] @@ -32,22 +33,20 @@ async def run(ctx: Context, prompt: MultiWeatherPrompt) -> str: llm_call, RunOptions(max_attempts=3), messages=messages, - tools=[get_weather], + tools=[get_weather_tool], ) + messages.append(response) - assistant_message = response.choices[0].message - messages.append(assistant_message) - - if not assistant_message.tool_calls: - return assistant_message.content + if not response.tool_calls: + return response.content # start all parallel tool calls with retries and recovery tool_output_promises = [] - for tool_call in assistant_message.tool_calls: + for tool_call in response.tool_calls: if tool_call.function.name == "get_weather": tool_promise = ctx.run_typed( "Get weather", - fetch_weather, + get_weather, req=WeatherRequest.model_validate_json( tool_call.function.arguments ), @@ -57,7 +56,7 @@ async def run(ctx: Context, prompt: MultiWeatherPrompt) -> str: ) # wait for all tool calls to complete - await restate.gather(*tool_output_promises) + await restate.gather(*[promise["promise"] for promise in tool_output_promises]) # gather the results and add to messages for tool_output_promise in tool_output_promises: @@ -67,6 +66,6 @@ async def run(ctx: Context, prompt: MultiWeatherPrompt) -> str: { "role": "tool", "tool_call_id": tool_output_promise["id"], - "content": tool_output, + "content": str(tool_output), } ) diff --git a/patterns/app/routing_to_agent.py b/patterns/app/routing_to_agent.py index 99ea6ab..fc12e63 100644 --- a/patterns/app/routing_to_agent.py +++ b/patterns/app/routing_to_agent.py @@ -76,7 +76,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: fn = tool_call.function # Route to appropriate support tool if fn.name == "billing_support": - return await ctx.run_typed( + result =await ctx.run_typed( "run billing agent", llm_call, restate.RunOptions(max_attempts=3), @@ -84,9 +84,10 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: "Acknowledge the billing issue, explain charges clearly, provide next steps with timeline." "Keep responses professional but friendly.", prompt=prompt.message, - ).content + ) + return result.content elif fn.name == "account_support": - return await ctx.run_typed( + result = await ctx.run_typed( "run account agent", llm_call, restate.RunOptions(max_attempts=3), @@ -94,9 +95,10 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: "Prioritize account security and verification, provide clear recovery steps, include security tips." "Maintain a serious, security-focused tone.", prompt=prompt.message, - ).content + ) + return result.content elif fn.name == "product_support": - return await ctx.run_typed( + result= await ctx.run_typed( "run product agent", llm_call, restate.RunOptions(max_attempts=3), @@ -104,6 +106,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: "Focus on feature education and best practices, include specific examples, suggest related features." "Be educational and encouraging in tone.", prompt=prompt.message, - ).content + ) + return result.content else: return "Sorry, I couldn't answer your request." diff --git a/patterns/app/routing_to_tool.py b/patterns/app/routing_to_tool.py index e333a04..a3b1e97 100644 --- a/patterns/app/routing_to_tool.py +++ b/patterns/app/routing_to_tool.py @@ -118,10 +118,12 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: ) # Final response to user based on tool result - return await ctx.run_typed( + response = await ctx.run_typed( "analyze tool output", llm_call, RunOptions(max_attempts=3, type_hint=Message), prompt=f"Provide a concise, friendly response to the user question {prompt.message} based on the tool output.", messages=messages, ) + + return response.content diff --git a/patterns/app/util/util.py b/patterns/app/util/util.py index a696949..12d8ed6 100644 --- a/patterns/app/util/util.py +++ b/patterns/app/util/util.py @@ -158,7 +158,7 @@ class Content(BaseModel): def notify_moderator(content: Content, approval_id: str): """Notify human moderator about content requiring review.""" print("\n🔍 CONTENT MODERATION REQUIRED 🔍") - print(f"Content: {request.request}") + print(f"Content: {content.message}") print(f"Awaiting human decision...") print("\nTo approve:") print( @@ -174,7 +174,7 @@ class WeatherRequest(BaseModel): city: str -async def fetch_weather(req: WeatherRequest) -> dict: +async def get_weather(req: WeatherRequest) -> dict: # This is a simulated failure to demo Durable Execution retries. try: resp = httpx.get( From 5b2c57edb72fa009a72002aaeeb5f687a8a80dcd Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Sun, 5 Oct 2025 19:30:22 +0200 Subject: [PATCH 3/6] Cleanup --- advanced/insurance-claims/README.md | 83 -- advanced/insurance-claims/app/__main__.py | 23 - advanced/insurance-claims/app/utils/models.py | 45 - advanced/insurance-claims/app/utils/utils.py | 48 - advanced/insurance-claims/app/workflow.py | 82 -- .../insurance-claims/data/dentist_receipt.pdf | Bin 60306 -> 0 bytes .../insurance-claims/data/doctor_receipt.pdf | Bin 46907 -> 0 bytes .../data/optician_receipt.pdf | Bin 50275 -> 0 bytes advanced/insurance-claims/pyproject.toml | 18 - advanced/insurance-claims/uv.lock | 459 ---------- advanced/restate-native-agent/README.md | 90 -- advanced/restate-native-agent/__main__.py | 29 - advanced/restate-native-agent/agent.py | 107 --- advanced/restate-native-agent/client.py | 21 - advanced/restate-native-agent/pyproject.toml | 17 - .../restate_agent/agent_session.py | 558 ------------ .../restate-native-agent/utils/__init__.py | 0 .../restate-native-agent/utils/account.py | 67 -- advanced/restate-native-agent/utils/models.py | 87 -- advanced/restate-native-agent/utils/utils.py | 97 --- advanced/restate-native-agent/uv.lock | 819 ------------------ patterns/app/__init__.py | 0 patterns/app/util/__init__.py | 0 {patterns => python-patterns}/README.md | 3 +- {patterns => python-patterns}/__main__.py | 0 .../utils => python-patterns/app}/__init__.py | 0 .../app/agent_with_tool.py | 0 {patterns => python-patterns}/app/chaining.py | 0 {patterns => python-patterns}/app/chat.py | 0 .../app/evaluator_optimizer.py | 0 .../app/human_in_the_loop.py | 0 .../app/orchestrator_workers.py | 0 .../app/parallel_agents.py | 0 .../app/parallel_tools.py | 0 .../app/routing_to_agent.py | 0 .../app/routing_to_remote_agent.py | 0 .../app/routing_to_tool.py | 0 .../app/util}/__init__.py | 0 .../app/util/litellm_call.py | 0 .../app/util/util.py | 0 {patterns => python-patterns}/pyproject.toml | 0 {patterns => python-patterns}/uv.lock | 0 42 files changed, 2 insertions(+), 2651 deletions(-) delete mode 100644 advanced/insurance-claims/README.md delete mode 100644 advanced/insurance-claims/app/__main__.py delete mode 100644 advanced/insurance-claims/app/utils/models.py delete mode 100644 advanced/insurance-claims/app/utils/utils.py delete mode 100644 advanced/insurance-claims/app/workflow.py delete mode 100644 advanced/insurance-claims/data/dentist_receipt.pdf delete mode 100644 advanced/insurance-claims/data/doctor_receipt.pdf delete mode 100644 advanced/insurance-claims/data/optician_receipt.pdf delete mode 100644 advanced/insurance-claims/pyproject.toml delete mode 100644 advanced/insurance-claims/uv.lock delete mode 100644 advanced/restate-native-agent/README.md delete mode 100644 advanced/restate-native-agent/__main__.py delete mode 100644 advanced/restate-native-agent/agent.py delete mode 100644 advanced/restate-native-agent/client.py delete mode 100644 advanced/restate-native-agent/pyproject.toml delete mode 100644 advanced/restate-native-agent/restate_agent/agent_session.py delete mode 100644 advanced/restate-native-agent/utils/__init__.py delete mode 100644 advanced/restate-native-agent/utils/account.py delete mode 100644 advanced/restate-native-agent/utils/models.py delete mode 100644 advanced/restate-native-agent/utils/utils.py delete mode 100644 advanced/restate-native-agent/uv.lock delete mode 100644 patterns/app/__init__.py delete mode 100644 patterns/app/util/__init__.py rename {patterns => python-patterns}/README.md (98%) rename {patterns => python-patterns}/__main__.py (100%) rename {advanced/insurance-claims/app/utils => python-patterns/app}/__init__.py (100%) rename {patterns => python-patterns}/app/agent_with_tool.py (100%) rename {patterns => python-patterns}/app/chaining.py (100%) rename {patterns => python-patterns}/app/chat.py (100%) rename {patterns => python-patterns}/app/evaluator_optimizer.py (100%) rename {patterns => python-patterns}/app/human_in_the_loop.py (100%) rename {patterns => python-patterns}/app/orchestrator_workers.py (100%) rename {patterns => python-patterns}/app/parallel_agents.py (100%) rename {patterns => python-patterns}/app/parallel_tools.py (100%) rename {patterns => python-patterns}/app/routing_to_agent.py (100%) rename {patterns => python-patterns}/app/routing_to_remote_agent.py (100%) rename {patterns => python-patterns}/app/routing_to_tool.py (100%) rename {advanced/restate-native-agent/restate_agent => python-patterns/app/util}/__init__.py (100%) rename {patterns => python-patterns}/app/util/litellm_call.py (100%) rename {patterns => python-patterns}/app/util/util.py (100%) rename {patterns => python-patterns}/pyproject.toml (100%) rename {patterns => python-patterns}/uv.lock (100%) diff --git a/advanced/insurance-claims/README.md b/advanced/insurance-claims/README.md deleted file mode 100644 index e1b8daa..0000000 --- a/advanced/insurance-claims/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Insurance Claims Processing - -This example shows how Restate can be used to write traditional workflows which incorporate LLM-based steps. - -Restate makes sure the end-to-end workflow runs to completion with retries and recovery of the LLM-based actions. - -The example shows how a PDF receipt gets interpreted by an LLM. If there is information missing, then the customer gets asked to supply this information. -This way, the LLM and the user collaborate to get to the final result of the filled-in claim. - -Insurance claims flow - -**Restate is a single workflow orchestrator that handles both agentic and traditional workflows and gives the same resiliency guarantees and observability across both.** - -Restate durably persists every step, whether the customer responds within a minute or after a month. - -Insurance claims UI - -## Running the example - -1. Export your OpenAI key as an environment variable: - ```shell - export OPENAI_API_KEY=your_openai_api_key - ``` -2. [Start the Restate Server](https://docs.restate.dev/develop/local_dev) in a separate shell: - ```shell - restate-server - ``` -3. Start the services: - ```shell - uv run app - ``` -4. Register the services: - ```shell - restate -y deployments register localhost:9080 --force - ``` - -5. Run the workflow with the receipts - - -We included a few example receipt PDFs in the `data` folder. - -Submit them as follows -```shell -curl localhost:8080/InsuranceClaimWorkflow/run --json '{"user_id": "johnDoe", "url": "data/doctor_receipt.pdf"}' -``` -Response: `{"date":"2025-05-21","amount":150.0,"description":"General Consultation and Blood Test","category":"medical","place_of_service":"Dr. Jane Smith"}` - -```shell -curl localhost:8080/InsuranceClaimWorkflow/run --json '{"user_id": "johnDoe", "url": "data/dentist_receipt.pdf"}' -``` -Response: `{"date":"2025-05-22","amount":1445.0,"description":"Periodic Oral Evaluation, Adult Prophylaxis (Cleaning), Crown - Porcelain/Ceramic, Pre-Treatment Exam","category":"dental","place_of_service":"Anytown Dental Clinic"}` - - -The optician receipt is a bit more tricky, because it has an unreadable date. -The LLM will ask the user to provide the date, as you see in the logs. - -```shell -curl localhost:8080/InsuranceClaimWorkflow/run --json '{"user_id": "rogerHugs", "url": "data/optician_receipt.pdf"}' -``` - -```text -[2025-05-21 17:12:09,335] [648143] [INFO] - Parsing PDF receipt for claim -[2025-05-21 17:12:11,192] [648143] [INFO] - HTTP Request: POST https://api.openai.com/v1/responses "HTTP/1.1 200 OK" -[2025-05-21 17:12:11,207] [648143] [INFO] - The following fields are missing: ['date'] -================================================== - Requesting more info for claim - The following information is missing: ['date']. Please provide the information as follows: - curl localhost:8080/restate/awakeables/sign_15KyvxGvGU_gBlvNm5mvREPXbsCH2Mr4pAAAAEQ/resolve --json '"Your message"' - ================================================== -``` - -Try killing the service and restarting it. Restate makes sure that the workflow progress is still persisted as you can see in the UI: - -Insurance claims UI - -To unblock the workflow we provide the date as described in the logs: -```shell -curl localhost:8080/restate/awakeables/sign_15KyvxGvGU_gBlvNm5mvREPXbsCH2Mr4pAAAAEQ/resolve --json '"02/02/2025"' -``` - -_(Write the date in the format ""MM/dd/YYYY")_ - -And finally get the response: `{"date":"2025-02-02","amount":511.5,"description":"Frames, Lenses (Pair), Anti-Reflective Coating, Premium Lens Cleaner","category":"vision","place_of_service":"Through your eyes - opticians, Dr. Eagle"}` \ No newline at end of file diff --git a/advanced/insurance-claims/app/__main__.py b/advanced/insurance-claims/app/__main__.py deleted file mode 100644 index 322291b..0000000 --- a/advanced/insurance-claims/app/__main__.py +++ /dev/null @@ -1,23 +0,0 @@ -import hypercorn -import asyncio -import restate -import logging - -from workflow import claim_workflow - -logging.basicConfig( - level=logging.INFO, - format="[%(asctime)s] [%(process)d] [%(levelname)s] - %(message)s", -) - - -def main(): - app = restate.app(services=[claim_workflow]) - - conf = hypercorn.Config() - conf.bind = ["0.0.0.0:9080"] - asyncio.run(hypercorn.asyncio.serve(app, conf)) - - -if __name__ == "__main__": - main() diff --git a/advanced/insurance-claims/app/utils/models.py b/advanced/insurance-claims/app/utils/models.py deleted file mode 100644 index 1fd7143..0000000 --- a/advanced/insurance-claims/app/utils/models.py +++ /dev/null @@ -1,45 +0,0 @@ -from enum import Enum -from pydantic import BaseModel - - -class ClaimRequest(BaseModel): - """ - A request to process a medical expense receipt. - - Attributes: - user_id (str): The user ID of the person making the claim. - url (str): The URL of the PDF file containing the claim information. - """ - - user_id: str - url: str - - -class ClaimCategory(str, Enum): - DENTAL = "dental" - MEDICAL = "medical" - VISION = "vision" - PRESCRIPTION = "prescription" - CHIROPRACTIC = "chiropractic" - MENTAL_HEALTH = "mental_health" - OTHER = "other" - - -class ClaimData(BaseModel): - """ - Data model for the claim data. - This model is used to store the information extracted from the claim PDF. - - Attributes: - date (str): The date of the claim in YYYY-MM-DD format. - amount (float): The amount of the claim. - description (str): A description of the claim. - category (ClaimCategory): The category of the claim. - place_of_service (str): The place where the service was provided: Name of Doctor, Clinic, etc. - """ - - date: str | None = None - amount: float | None = None - description: str | None = None - category: ClaimCategory | None = None - place_of_service: str | None = None diff --git a/advanced/insurance-claims/app/utils/utils.py b/advanced/insurance-claims/app/utils/utils.py deleted file mode 100644 index aaab573..0000000 --- a/advanced/insurance-claims/app/utils/utils.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging -import uuid - -from pypdf import PdfReader - -from .models import ClaimData - -logger = logging.getLogger(__name__) - - -def extract_text_from_pdf(url: str) -> str: - logger.info("Parsing PDF receipt for claim") - pdf_reader = PdfReader(url) - text = "" - for page_num in range(len(pdf_reader.pages)): - text += pdf_reader.pages[page_num].extract_text() - return text - - -def create_claim(claim: ClaimData) -> str: - # Simulate creating a claim in a legacy system - # In a real-world scenario, this would involve making an API call to the legacy system - # and returning the assigned claim ID. - logger.info("Creating claim in legacy system: %s", claim.model_dump_json()) - return uuid.uuid4().hex - - -def send_message_to_customer(missing_fields: list[str], id: str) -> None: - print( - "=" * 50, - f"\n Requesting more info for claim \n", - f"The following information is missing: {missing_fields}. Please provide the information as follows:\n", - f"curl localhost:8080/restate/awakeables/{id}/resolve --json '\"Your message\"' \n", - "=" * 50, - ) - - -def check_missing_fields(claim: ClaimData) -> list[str]: - # Check if all required fields are present in the claim data - required_fields = ClaimData.model_fields.keys() - missing_fields = [] - for field in required_fields: - if getattr(claim, field) is None: - missing_fields.append(field) - - if missing_fields: - logger.info("The following fields are missing: %s", missing_fields) - return missing_fields diff --git a/advanced/insurance-claims/app/workflow.py b/advanced/insurance-claims/app/workflow.py deleted file mode 100644 index 15089b0..0000000 --- a/advanced/insurance-claims/app/workflow.py +++ /dev/null @@ -1,82 +0,0 @@ -import logging -import restate - -from openai import OpenAI - -from utils.models import ClaimRequest, ClaimData -from utils.utils import ( - extract_text_from_pdf, - check_missing_fields, - send_message_to_customer, - create_claim, -) - -logger = logging.getLogger(__name__) - -client = OpenAI() - -claim_workflow = restate.Service("InsuranceClaimWorkflow") - - -@claim_workflow.handler() -async def run(ctx: restate.Context, req: ClaimRequest) -> ClaimData: - """ - Resilient LLM-based insurance claim workflow that processes a PDF receipt and extracts claim data. - It checks for missing fields and requests additional information from the customer if needed. - """ - - # Parse the PDF receipt - raw_claim = await ctx.run_typed("Read PDF", extract_text_from_pdf, url=req.url) - - def parse_claim_data(raw_claim_input: str, extra_info_input: str = "") -> ClaimData: - return ( - client.responses.parse( - model="gpt-4o-2024-08-06", - input=[ - {"role": "system", "content": "Extract the claim information."}, - { - "role": "user", - "content": f"Claim data: {raw_claim_input} Extra info: {extra_info_input}", - }, - ], - text_format=ClaimData, - ).output_parsed - or ClaimData() - ) - - # Extract claim data - claim = await ctx.run_typed( - "Extracting", - parse_claim_data, - restate.RunOptions(type_hint=ClaimData), - raw_claim_input=raw_claim, - ) - - # Repetitively check for missing fields and request additional information if needed - while True: - missing_fields = await ctx.run_typed( - "completeness check", check_missing_fields, claim=claim - ) - if not missing_fields: - break - - id, promise = ctx.awakeable(type_hint=str) - await ctx.run_typed( - "Request missing info", - send_message_to_customer, - missing_fields=missing_fields, - id=id, - ) - extra_info = await promise - - claim = await ctx.run_typed( - "Extracting", - parse_claim_data, - restate.RunOptions(type_hint=ClaimData), - raw_claim_input=raw_claim, - extra_info_input=extra_info, - ) - - # Create the claim in the legacy system - await ctx.run_typed("create", lambda: create_claim(claim)) - return claim diff --git a/advanced/insurance-claims/data/dentist_receipt.pdf b/advanced/insurance-claims/data/dentist_receipt.pdf deleted file mode 100644 index b1ca5ed5df4886b443006b9dfd32bbf8ac10a4d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60306 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 Vwg1=r42+0oVS^_p7m^W%|3BaNGz$O# diff --git a/advanced/insurance-claims/data/doctor_receipt.pdf b/advanced/insurance-claims/data/doctor_receipt.pdf deleted file mode 100644 index ef24f005bd923834dd8298b2a31f16c7cd7f7c1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46907 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 diff --git a/advanced/insurance-claims/data/optician_receipt.pdf b/advanced/insurance-claims/data/optician_receipt.pdf deleted file mode 100644 index 26611886f93c3dcbfc184d16841126874773c0e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50275 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 diff --git a/advanced/insurance-claims/pyproject.toml b/advanced/insurance-claims/pyproject.toml deleted file mode 100644 index 6fe5e3c..0000000 --- a/advanced/insurance-claims/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[project] -name = "insurance-claims" -version = "0.1.0" -description = "Restate insurance claims example" -requires-python = ">=3.12" - -dependencies = [ - "hypercorn", - "restate_sdk[serde]>=0.10.2", - "pydantic>=2.11.9", - "openai", - "pypdf" -] - -[dependency-groups] -dev = [ - "mypy>=1.18.2", -] diff --git a/advanced/insurance-claims/uv.lock b/advanced/insurance-claims/uv.lock deleted file mode 100644 index d9766a8..0000000 --- a/advanced/insurance-claims/uv.lock +++ /dev/null @@ -1,459 +0,0 @@ -version = 1 -revision = 1 -requires-python = ">=3.12" - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, -] - -[[package]] -name = "anyio" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, -] - -[[package]] -name = "certifi" -version = "2025.4.26" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "dacite" -version = "1.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/55/a0/7ca79796e799a3e782045d29bf052b5cde7439a2bbb17f15ff44f7aacc63/dacite-1.9.2.tar.gz", hash = "sha256:6ccc3b299727c7aa17582f0021f6ae14d5de47c7227932c47fec4cdfefd26f09", size = 22420 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/35/386550fd60316d1e37eccdda609b074113298f23cef5bddb2049823fe666/dacite-1.9.2-py3-none-any.whl", hash = "sha256:053f7c3f5128ca2e9aceb66892b1a3c8936d02c686e707bee96e19deef4bc4a0", size = 16600 }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, -] - -[[package]] -name = "h2" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, -] - -[[package]] -name = "hypercorn" -version = "0.17.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, - { name = "h2" }, - { name = "priority" }, - { name = "wsproto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/3a/df6c27642e0dcb7aff688ca4be982f0fb5d89f2afd3096dc75347c16140f/hypercorn-0.17.3.tar.gz", hash = "sha256:1b37802ee3ac52d2d85270700d565787ab16cf19e1462ccfa9f089ca17574165", size = 44409 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/3b/dfa13a8d96aa24e40ea74a975a9906cfdc2ab2f4e3b498862a57052f04eb/hypercorn-0.17.3-py3-none-any.whl", hash = "sha256:059215dec34537f9d40a69258d323f56344805efb462959e727152b0aa504547", size = 61742 }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] - -[[package]] -name = "insurance-claims" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "hypercorn" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "pypdf" }, - { name = "restate-sdk", extra = ["serde"] }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, -] - -[package.metadata] -requires-dist = [ - { name = "hypercorn" }, - { name = "openai" }, - { name = "pydantic", specifier = ">=2.11.9" }, - { name = "pypdf" }, - { name = "restate-sdk", extras = ["serde"], specifier = ">=0.10.2" }, -] - -[package.metadata.requires-dev] -dev = [{ name = "mypy", specifier = ">=1.18.2" }] - -[[package]] -name = "jiter" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, -] - -[[package]] -name = "mypy" -version = "1.18.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273 }, - { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910 }, - { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585 }, - { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562 }, - { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296 }, - { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828 }, - { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728 }, - { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758 }, - { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342 }, - { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709 }, - { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806 }, - { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262 }, - { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775 }, - { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852 }, - { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242 }, - { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683 }, - { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749 }, - { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959 }, - { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, -] - -[[package]] -name = "openai" -version = "1.86.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/7a/9ad4a61f1502f0e59d8c27fb629e28a63259a44d8d31cd2314e1534a2d9f/openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b", size = 468272 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/c1/dfb16b3432810fc9758564f9d1a4dbce6b93b7fb763ba57530c7fc48316d/openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349", size = 730296 }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - -[[package]] -name = "priority" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946 }, -] - -[[package]] -name = "pydantic" -version = "2.11.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855 }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, -] - -[[package]] -name = "pypdf" -version = "5.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/46/67de1d7a65412aa1c896e6b280829b70b57d203fadae6859b690006b8e0a/pypdf-5.6.0.tar.gz", hash = "sha256:a4b6538b77fc796622000db7127e4e58039ec5e6afd292f8e9bf42e2e985a749", size = 5023749 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/8b/dc3a72d98c22be7a4cbd664ad14c5a3e6295c2dbdf572865ed61e24b5e38/pypdf-5.6.0-py3-none-any.whl", hash = "sha256:ca6bf446bfb0a2d8d71d6d6bb860798d864c36a29b3d9ae8d7fc7958c59f88e7", size = 304208 }, -] - -[[package]] -name = "restate-sdk" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/93/07/60d1a5b5d87cb7711c12dc1333b26fc1e41aa9af68d1ee400ad052aaaf75/restate_sdk-0.10.2.tar.gz", hash = "sha256:6a22de5506952b1977bc5a7c03ce674a6103033d112a4266bc91bb55c0a8ab81", size = 69388 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/d3/11c40e63429ae60685423e3ced99c58bacc3ce58119f4e0b7f376664cfe0/restate_sdk-0.10.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e59e4a9c31e45ee151ee9c68ebadd441f01c96812aaab4da319ca3fa28c2b5d8", size = 1810525 }, - { url = "https://files.pythonhosted.org/packages/cc/1a/7dd5800b28a40a440360ae182329f713cdba874fdaa473046d992ee9cbbc/restate_sdk-0.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d8997534628e7d782d2d989422e5cdee2a9667c5a1b4d519338c6db90aef607", size = 1746507 }, - { url = "https://files.pythonhosted.org/packages/7b/af/586b838eee60ead8a16193b0130b25034b5cd3a44bd0397c64e5014ac8e8/restate_sdk-0.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5b463dc5563ffcb3a40420e3f652c6b83e093200a808a6132940b44b5adb37b", size = 1944292 }, - { url = "https://files.pythonhosted.org/packages/c4/3d/7efe71c9654d3d74122437080851b0b357f3e6c119ad2b560f9b6cda308b/restate_sdk-0.10.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5f33316c1b514479fa7601d64d1dd51ecef0e0135ce1e68bfe3c46fe9fb4acac", size = 1973185 }, - { url = "https://files.pythonhosted.org/packages/91/22/ae77bd4b5ed2a609b01b028204b4793a0725ebdf9f44d5e56d1661bd2918/restate_sdk-0.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9732e7189b389f0a8e1b55bdadb167daf5654dd31c0404c78f4698b7a3b9244", size = 2166461 }, - { url = "https://files.pythonhosted.org/packages/47/61/c2f665e244a2c489339dcaa4ec022685752639a0e372474b3ba86cec9579/restate_sdk-0.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2108f4466fc62cbbcd9e4f55333a8a5d9167034d70b32929c3c2a61978030007", size = 2181011 }, - { url = "https://files.pythonhosted.org/packages/47/1b/5ec4f3d8e664be31ca07badcc101d4faafd97f5fe5dfd2de2048d7ddbef9/restate_sdk-0.10.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:706586b308b7d7d870239d25ce3567f1021895b611f35fcd0153a8893c300abc", size = 1810372 }, - { url = "https://files.pythonhosted.org/packages/6c/d9/4ec42ecfb4c08b5c3c1dcb0ffd73339e54ab6598d246f6b4eba292e83b94/restate_sdk-0.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1295308a1735707514c3ea20972f687c1815b5bfde99beb808a1aca3fd820f0a", size = 1746500 }, - { url = "https://files.pythonhosted.org/packages/0c/91/4992cf27251d8a4c6aa1778761d5f7e2b883e7433d981568912ce62ed29b/restate_sdk-0.10.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16fb5b91d89e69ba970f3eb4be286fc432dd560a20d9f703a300c7d9a3794997", size = 1943925 }, - { url = "https://files.pythonhosted.org/packages/98/42/060683f6e6188327dd935c13a2e7a562f61ff48d1e73482a0ecc25580518/restate_sdk-0.10.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a5c6bf798d7e95b3cca2b0881e5e7f5b8f343f09900a19018946e3459ece8d2c", size = 1973705 }, - { url = "https://files.pythonhosted.org/packages/d6/88/d70d359e97fc6cf50d400dc6bef7fee23983de7adeab65b512a74a0d7232/restate_sdk-0.10.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:196ae6de4319350118bc9b991f0f9bb306fe3b6544be30a130dc766cfd256ae5", size = 2167041 }, - { url = "https://files.pythonhosted.org/packages/a3/d9/e7e1d7cc26fff8639f0513e36eb0d995fac531dd340ca4996d5d301201d6/restate_sdk-0.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b166a0a3b87184d67a926d32e1998540bcd5af538002ac481e61f1e1d2a35a94", size = 2181127 }, - { url = "https://files.pythonhosted.org/packages/1c/07/cfbc53f1090673da297985aa149bf8a6c2469c9481fbe5e6b79362124bd3/restate_sdk-0.10.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:6a4078872db1422496b18c3691b5dc84ef7a65dde7254017f15d9591f201390b", size = 1968738 }, - { url = "https://files.pythonhosted.org/packages/bc/67/a7ee5f9a7a84f11c287e6ed457906ad099733d70f1685c6a331ad9eafcc8/restate_sdk-0.10.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:940b37b450a20484a71f688db2b275bdf1558c95866b294939fd7195bc27af1f", size = 2165326 }, - { url = "https://files.pythonhosted.org/packages/fd/76/3955e6505f27d30d5c4b3ff6a96911e213cefa1cd0c3b437ff0fda151812/restate_sdk-0.10.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7b651625c0476c45c6cde29507c4fb499994c7ae256166b26243725f581ba384", size = 2179261 }, - { url = "https://files.pythonhosted.org/packages/94/ff/7439fb22cdb9f3ca1cb620923cc2f0221518a3d9df2ca08611010fddec3b/restate_sdk-0.10.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bbc6df7efcf77b6d283c84635cae6cd3c9485144dc9bd7b42c1bffbf15c10c4", size = 1942989 }, -] - -[package.optional-dependencies] -serde = [ - { name = "dacite" }, - { name = "pydantic" }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, -] - -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, -] - -[[package]] -name = "typing-extensions" -version = "4.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, -] - -[[package]] -name = "wsproto" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 }, -] diff --git a/advanced/restate-native-agent/README.md b/advanced/restate-native-agent/README.md deleted file mode 100644 index 1c9f719..0000000 --- a/advanced/restate-native-agent/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Restate-native agent implementation - -This is an example of how far you can go with customizing your agentic workflows with Restate. - -This example implements the agent loop directly with Restate, providing maximum control over the agent's behavior and workflow execution. - -It provides full customization possibilities. For example, you can extend it to support things like interruptible agents and [saga patterns](https://docs.restate.dev/guides/sagas). and supports parallel tool calls, but requires managing and writing the agent loop yourself. - -**The agent composes the workflow on the fly, and Restate persists the execution as it takes place.** - -Restate powers your agents with the following features: -- 🛡️ **Automatic retries**: Built-in retry mechanisms for failed operations -- 🔐 **Recovery of decisions and tool results**: Restate retries only the failed step and preserves the rest of the progress -- 🔄 **Stateful agent sessions**: Isolate different sessions/conversations with Virtual Objects. Get isolated memory and concurrency guarantees per object. Memory is queryable from the outside and viewable in the Restate UI -- 🚀 **Scalability**: Parallel sessions with consistent state -- 🔍 **Observability**: Via the Restate UI and OTEL traces, you get line-by-line execution tracking and insight into tool executions and hand-off chains -- ⏱️ **Long-running Agentic Workflows**: Durability for any workflow from millis to months. And built-in durable timers & scheduling -- 🙂 **Resilient human-in-the-loop**: Both approaches support human intervention in workflows -- 👬 **Idempotency/deduplication**: Prevents duplicate agent requests - -If we implement the agent loop with Restate, Restate journals each of the decisions the agents make and manages the tool executions. -The agent session is a Restate Virtual Object that has a handler that runs the agent loop. - -Agentic workflow - -## Running the example - -This example implements a bank agent that can answer questions about your balance, loans and transactions. - -1. Export your OpenAI or Anthrophic API key as an environment variable: - ```shell - export OPENAI_API_KEY=your_openai_api_key - ``` -2. [Start the Restate Server](https://docs.restate.dev/develop/local_dev) in a separate shell: - ```shell - restate-server - ``` -3. Start the service: - ```shell - uv run . - ``` -4. Register the services: - ```shell - restate -y deployments register localhost:9080 --force - ``` - -Now you can send requests to the agent via the UI playground (click on the agent service and then `playground`): - -UI example - -Or with the [client](client.py): - -- **Request**: - ```shell - uv run client.py "how much is my balance?" - ``` - Example response: `Your current balance is $100,000.00. If you have any other questions, feel free to ask!` - - UI example - -- **Request**: - ```shell - uv run client.py "how much did I spend on gambling last month?" - ``` - Example response: `I reviewed your transactions from last month, and it appears you didn't spend any money on gambling during that period. If you have any other questions or need further clarification, please let me know!` - -- **Request**: - - ```shell - uv run client.py "give me an overview of my outstanding loans and credit" - ``` - - Example response: - ``` - Here's an overview of your outstanding loans: - - 1. **Car Purchase Loan** - - **Amount**: $10,000 - - **Duration**: 12 months - - **Approved**: Yes - - **Reason**: Good credit score and no risky transactions like gambling. - - **Monthly Payment**: $9,856.07 - - **Months Left**: 11 - - If you need more information, feel free to ask! - ``` - -You can see the state of your agent in the state tab in the UI: - -UI state \ No newline at end of file diff --git a/advanced/restate-native-agent/__main__.py b/advanced/restate-native-agent/__main__.py deleted file mode 100644 index 80794be..0000000 --- a/advanced/restate-native-agent/__main__.py +++ /dev/null @@ -1,29 +0,0 @@ -import hypercorn -import asyncio -import restate -import logging - -from agent import agent -from utils.account import account - -logging.basicConfig( - level=logging.INFO, - format="[%(asctime)s] [%(process)d] [%(levelname)s] - %(message)s", -) - - -def main(): - app = restate.app( - services=[ - agent, - account, - ] - ) - - conf = hypercorn.Config() - conf.bind = ["0.0.0.0:9080"] - asyncio.run(hypercorn.asyncio.serve(app, conf)) - - -if __name__ == "__main__": - main() diff --git a/advanced/restate-native-agent/agent.py b/advanced/restate-native-agent/agent.py deleted file mode 100644 index b453f77..0000000 --- a/advanced/restate-native-agent/agent.py +++ /dev/null @@ -1,107 +0,0 @@ -import restate - -from utils.account import ( - get_balance, - get_transaction_history, - get_customer_loans, -) -from restate_agent.agent_session import ( - run_agent as agent_session_run, - AgentInput, - restate_tool, - Agent, - RECOMMENDED_PROMPT_PREFIX, -) - -# AGENTS - -bank_agent = Agent( - name="Bank Agent", - handoff_description="A helpful agent that can helps you with answering questions about your bank account: the balance and transaction history.", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - You are an agent that helps with answering questions about the bank account: related to the balance and transaction history. - You are not able to help with anything else. - If you are speaking to a customer, you probably were transferred to from the intake agent. - You have the tools to retrieve the transaction history and the balance. - Use the following routine to support the customer. - # Routine #1 - 1. Make sure you know the customer ID. This will be the key for all interaction with the tools. - Never use another key as the customer ID you find at the top of the chat message. - 2. Use the get_balance and get_transaction_history tools to retrieve the balance or the transaction history of the customer. - Depending on the customer question, use the right tool. - 3. Analyze the response and return the right information to the customer in a kind, polite, formal message. - 4. If the customer asks a question that is not related to these routines, transfer back to the intake agent. - """, - tools=[ - restate_tool(get_balance), - restate_tool(get_transaction_history), - ], -) - -loan_agent = Agent( - name="Loan Agent", - handoff_description="A helpful agent that can helps you with retrieving the status of a loan, and the decision made (approval and reason).", - instructions=f"""{RECOMMENDED_PROMPT_PREFIX} - You are an agent that helps with: - - giving information on the status of the loan requests - - giving information on the decision made (approval and reason) of the loan requests - - giving information on the status of the loan payments (monthly amount, months left) - You are not able to help with anything else. - If you are speaking to a customer, you probably were transferred to from the intake agent. - You have the tools to either create new loan requests and retrieve the loan status and decision. - Use the following routine to support the customer. - # Routine #1 - If the customer wants to get the status of his ongoing loan payments, loan requests, and loan decisions: - 1. Make sure you know the customer ID. - 2. Use the get_customer_loans tool to retrieve the status of the loans of the customer. - 3. Retrieve the right information from the response and return it to the customer. - - 3. If the customer asks a question that is not related to these routines, transfer back to the intake agent. - """, - tools=[ - restate_tool(get_customer_loans), - ], -) - -intake_agent = Agent( - name="Intake Agent", - handoff_description="An intake agent that can delegates a customer's request to the appropriate agent.", - instructions=( - f"{RECOMMENDED_PROMPT_PREFIX}" - "You are a helpful intake agent. You can use your handoffs to delegate questions to other appropriate agents." - ), - handoffs=[loan_agent.name, bank_agent.name], -) - -loan_agent.handoffs.append(intake_agent.name) -bank_agent.handoffs.append(intake_agent.name) - -chat_agents = [bank_agent, loan_agent, intake_agent] - -# AGENT - -# Keyed by customer id -agent = restate.VirtualObject("Agent") - - -@agent.handler() -async def run(ctx: restate.ObjectContext, req: str) -> str: - """ - Send a message to the agent. - - Args: - req (str): The message to send to the agent. - - Returns: - str: The response from the agent. - """ - result = await agent_session_run( - ctx, - AgentInput( - starting_agent=intake_agent, - agents=chat_agents, - message=f"For customer ID {ctx.key()}: {req}", # this is the input for the LLM call - ), - ) - - return result.final_output diff --git a/advanced/restate-native-agent/client.py b/advanced/restate-native-agent/client.py deleted file mode 100644 index adb21d8..0000000 --- a/advanced/restate-native-agent/client.py +++ /dev/null @@ -1,21 +0,0 @@ -import sys - -import httpx - - -def run(): - if len(sys.argv) == 0: - raise ValueError("No input provided") - data = sys.argv[1] - - r = httpx.post( - "http://localhost:8080/Agent/my-user/run", - json=data, - timeout=60, - ) - r.raise_for_status() - - print(r.json()) - - -run() diff --git a/advanced/restate-native-agent/pyproject.toml b/advanced/restate-native-agent/pyproject.toml deleted file mode 100644 index 8cd75b2..0000000 --- a/advanced/restate-native-agent/pyproject.toml +++ /dev/null @@ -1,17 +0,0 @@ -[project] -name = "agents" -version = "0.1.0" -description = "Python examples using Restate for agents" -requires-python = ">=3.12" - -dependencies = [ - "hypercorn", - "restate_sdk[serde]>=0.10.1", - "pydantic>=2.11.9", - "openai-agents==0.3.1", -] - -[dependency-groups] -dev = [ - "mypy>=1.18.2", -] diff --git a/advanced/restate-native-agent/restate_agent/agent_session.py b/advanced/restate-native-agent/restate_agent/agent_session.py deleted file mode 100644 index 19536d7..0000000 --- a/advanced/restate-native-agent/restate_agent/agent_session.py +++ /dev/null @@ -1,558 +0,0 @@ -import json -import logging -import uuid -import httpx -import restate - -from datetime import timedelta -from typing import ( - Optional, - Any, - Callable, - Awaitable, - TypeVar, - Type, - List, - Literal, - TypedDict, - Dict, -) - -from openai import OpenAI -from openai.lib._pydantic import to_strict_json_schema -from openai.types.responses import ( - ResponseFunctionToolCall, - Response, - ResponseOutputMessage, - ResponseOutputItem, - EasyInputMessage -) -from pydantic import BaseModel, ConfigDict, Field -from restate import TerminalError -from restate.handler import handler_from_callable -from typing_extensions import Generic - -logger = logging.getLogger(__name__) - -I = TypeVar("I", bound=BaseModel) -O = TypeVar("O", bound=BaseModel) - -client = OpenAI() - -# PROMPTS - -# prompt prefix for agents -RECOMMENDED_PROMPT_PREFIX = ( - "# System context\n" - "You are part of a multi-agent system called the Agents SDK, designed to make agent " - "coordination and execution easy. Agents uses two primary abstraction: **Agents** and " - "**Handoffs**. An agent encompasses instructions and tools and can hand off a " - "conversation to another agent when appropriate. " - "Handoffs are achieved by calling a handoff function, generally having the word '_agent' in their name. " - "Transfers between agents are handled seamlessly in the background;" - " do not mention or draw attention to these transfers in your conversation with the user.\n" - "You can run tools in parallel but you can only hand off to at most one agent. Never suggest handing off to two or more." - "If you want to run a tool, then do this before generating the output message. Never return a tool call together with an output message." -) - -# prompt prefix for tools; gets added to the tool description -VIRTUAL_OBJECT_HANDLER_TOOL_PREFIX = ( - "# System context\n" - "This tool is part of a Virtual Object. Virtual Objects are keyed services that need to be addressed by specifying the key. " - "The key is a unique identifier for the object. " - "The key makes sure that you get access to the correct object so it is really important that this is correct. " - "The key is a string. In case there is the slightest doubt about the key, always ask the user for the key. " - "The key is part of the input schema of the tool. You can find the meaning of the key in the tool's input schema. " - "Keys usually present a unique identifier for the object: for example a customer virtual object might have the customer id as key. " - "Unless the agent really explicitly asks to only schedule the task, set delay to None because then you will be able to retrieve the response." -) - -WORKFLOW_HANDLER_TOOL_PREFIX = ( - "# System context\n" - "This tool is part of a Workflow. Workflows are keyed services that need to be addressed by specifying the key. " - "The key is a unique identifier for the workflow. " - "The key makes sure that you get access to the correct workflow so it is really important that this is correct. " - "The key is a string. In case there is the slightest doubt about the key, always ask the user for the key. " - "The key is part of the input schema of the tool. You can find the meaning of the key in the tool's input schema. " - "Keys usually present a unique identifier for the workflow: for example a customer signup workflow might have the customer id as key. " - "Unless the agent really explicitly asks to wait for the final result of the workflow, set delay to 0 because this will submit the workflow without waiting for the response." -) - - -# MODELS AND TYPES -class Empty(BaseModel): - model_config = ConfigDict(extra="forbid") - - -class AgentError(Exception): - """ - Errors that should be fed back into the next agent loop. - """ - - def __init__(self, message: str): - self.message = message - super().__init__(f"Agent Error: {message}") - - -class RestateRequest(BaseModel, Generic[I]): - """ - Represents a request to a Restate service. - - Attributes: - key (str): The unique identifier for the Virtual Object or Workflow which contains the tool. - req (I): The request to be passed to the tool. - delay_in_millis (int): The delay in milliseconds to delay the task with. - """ - - key: str - req: I | Empty - delay_in_millis: int | None - - -class RestateTool(BaseModel, Generic[I, O]): - """ - Represents a Restate tool. - - Attributes: - service_name (str): The name of the service that provides the tool. - name (str): The name of the tool, equal to the name of the service handler. - description (str): A description of the tool, to be used by the agent. - service_type (str): The type of the service (service, object, or workflow). - tool_schema (dict[str, Any]): The schema for the tool's input and output. - formatted_name (str): The formatted name of the tool without spaces and lowercase, used for the LLM. - """ - - service_name: str - name: str - description: str - service_type: str - tool_schema: dict[str, Any] - formatted_name: str = Field(default_factory=lambda data: format_name(data["name"])) - - -class Agent(BaseModel): - """ - Represents an agent in the system. - - Attributes: - name (str): The name of the agent. - handoff_description (str): A description of the agent, to be used by the LLM. - instructions (str): Instructions for the agent, to be used by the LLM. - tools (list[RestateTool]): A list of tools that the agent can use. - handoffs (list[str]): A list of handoff agents to which the agent can hand off the conversation. - formatted_name (str): The formatted name of the agent without spaces and lowercase, used for the LLM. - """ - - name: str - handoff_description: str - instructions: str | None = None - tools: list[RestateTool] = Field(default_factory=list) - handoffs: list[str] = Field(default_factory=list) # agent names - formatted_name: str = Field(default_factory=lambda data: format_name(data["name"])) - - def to_tool_schema(self) -> dict[str, Any]: - schema = to_strict_json_schema(Empty) - return { - "type": "function", - "name": f"{format_name(self.name)}", - "description": self.handoff_description, - "parameters": schema, - "strict": True, - } - - def as_tool(self, name: str, description: str): - tool = restate_tool(run_agent_session) - tool.description = ( - f"{description} \n {self.handoff_description} \n {tool.description}" - ) - tool.name = f"{name}_as_tool" - return tool - - -class AgentInput(BaseModel): - """ - The input of an agent session run. - - Attributes: - starting_agent (Agent): the agent to start the interaction with - agents (list[Agent]): all the agents that can be part of the interaction - message (str): input message for the agent - """ - - starting_agent: Agent - agents: list[Agent] - message: str - - -class AgentResponse(BaseModel): - """ - Represents the response from an agent session. - - Attributes: - agent (str): The name of the agent that generated the response. - messages (list[dict[str, Any]]): The messages generated during the session. - final_output (str): The final output of the agent. - """ - - agent: Optional[str] - messages: list[dict[str, Any]] - final_output: str - - -class ToolCall(BaseModel): - name: str - tool: RestateTool - key: str | None - input_bytes: bytes - delay_in_millis: int | None - - -class SessionItem(TypedDict): - """ - Represents a single item in the session. - - Attributes: - role (str): The role of who generated the item, either "user", "assistant", or "system". - content (str): The content of the item. - """ - - role: Literal["user", "assistant", "system"] - content: str - - -class SessionState: - """ - Represents the state of the session. - """ - - def __init__(self, input_items: Optional[List[SessionItem]] = None): - self._input_items: List[SessionItem] = input_items or [] - self._new_items: List[SessionItem] = [] - self.state_name = "agent_state" - - def add_user_message(self, ctx: restate.ObjectContext, item: str): - user_message = SessionItem(content=item, role="user") - self._input_items.append(user_message) - ctx.set(self.state_name, self._input_items) - self._new_items.append(user_message) - - def add_user_messages(self, ctx: restate.ObjectContext, items: List[str]): - user_messages = [SessionItem(content=item, role="user") for item in items] - self._input_items.extend(user_messages) - ctx.set(self.state_name, self._input_items) - self._new_items.extend(user_messages) - - def add_system_message(self, ctx: restate.ObjectContext, item: str): - system_message = SessionItem(content=item, role="system") - self._input_items.append(system_message) - self._new_items.append(system_message) - ctx.set(self.state_name, self._input_items) - - def add_system_messages(self, ctx: restate.ObjectContext, items: List[str]): - system_messages = [SessionItem(content=item, role="system") for item in items] - self._input_items.extend(system_messages) - ctx.set(self.state_name, self._input_items) - self._new_items.extend(system_messages) - - def get_input_items(self) -> List[SessionItem]: - return self._input_items - - def get_new_items(self) -> List[SessionItem]: - return self._new_items - - -# AGENT SESSION -# Option 1: run the agent session as a separate service -agent_session = restate.VirtualObject("AgentSession") - - -@agent_session.handler() -async def run_agent_session( - ctx: restate.ObjectContext, req: AgentInput -) -> AgentResponse: - return await run_agent(ctx, req) - - -# Option 2: call this method immediately from the chat session/workflow -async def run_agent(ctx: restate.ObjectContext, req: AgentInput) -> AgentResponse: - """ - Runs an end-to-end agent interaction: - 1. calls the LLM with the input - 2. runs all tools and handoffs - 3. keeps track of the session data: history and current agent - - returns the new items generated - - Args: - req (AgentInput): The input for the agent - """ - log_prefix = f"{ctx.request().id} - agent-session {ctx.key()}- - " - - # === 1. initialize the agent session === - logging.info(f"{log_prefix} Starting agent session") - session_state = SessionState(input_items=await ctx.get("agent_state")) - session_state.add_user_message(ctx, req.message) - - agent_name = await ctx.get("agent_name", type_hint=str) or req.starting_agent.formatted_name - ctx.set("agent_name", agent_name) - - agents_dict = {a.formatted_name: a for a in req.agents} - - if agent_name not in agents_dict: - raise TerminalError( - f"Agent {agent_name} not found in the list of agents: {list(agents_dict.keys())}" - ) - agent = agents_dict[agent_name] - - - # === 2. Run the agent loop === - while True: - # Get the tools in the right format for the LLM - try: - tools = {tool.formatted_name: tool for tool in agent.tools} - logger.info( - f"{log_prefix} Starting iteration of agent: {agent.name} with tools/handoffs: {list(tools.keys())}" - ) - - tool_schemas = await generate_tool_schemas(agent, agents_dict) - - # Call the LLM - OpenAPI Responses API - async def call_llm(session_items: list[SessionItem]) -> Response: - resp = client.responses.create( - model="gpt-4o", - instructions=agent.instructions, - input=session_items, # type: ignore - tools=tool_schemas, # type: ignore - parallel_tool_calls=True, - stream=False, - ) - if not isinstance(resp, Response): - raise TerminalError(f"LLM response is not of type Response: {type(resp)}") - return resp - logger.info(f"{log_prefix} Calling LLM") - response = await ctx.run_typed( - "Call LLM", - call_llm, - restate.RunOptions(max_attempts=3, type_hint=Response), # To avoid using too many credits on infinite retries during development - session_items=session_state.get_input_items() - ) - - # Register the output in the session state - session_state.add_system_messages( - ctx, [item.model_dump_json() for item in response.output] - ) - - # Parse LLM response - output_messages, run_handoffs, tool_calls = await parse_llm_response( - agents_dict, response.output, tools - ) - - # Execute (parallel) tool calls - parallel_tools = [] - for tool_call in tool_calls: - logger.info(f"{log_prefix} Executing tool {tool_call.name}") - try: - if tool_call.delay_in_millis is None: - handle = ctx.generic_call( - service=tool_call.tool.service_name, - handler=tool_call.tool.name, - arg=tool_call.input_bytes, - key=tool_call.key, - ) - parallel_tools.append(handle) - else: - # Used for scheduling tasks in the future or long-running tasks like workflows - ctx.generic_send( - service=tool_call.tool.service_name, - handler=tool_call.tool.name, - arg=tool_call.input_bytes, - key=tool_call.key, - send_delay=timedelta( - milliseconds=tool_call.delay_in_millis - ), - ) - session_state.add_system_message( - ctx, f"Task {tool_call.name} was scheduled" - ) - except TerminalError as e: - # We add it to the session_state to feed it back into the next LLM call - # Let the other parallel tool executions continue - logger.warning(f"Failed to execute tool {tool_call.name}: {str(e)}") - session_state.add_system_message( - ctx, - f"Failed to execute tool {tool_call.name}: {str(e)}", - ) - - if len(parallel_tools) > 0: - results_done = await restate.gather(*parallel_tools) - results = [(await result).decode() for result in results_done] - logger.info(f"{log_prefix} Gathered tool results.") - session_state.add_system_messages(ctx, results) - - # Handle handoffs - if run_handoffs: - # Only one agent can be in charge of the conversation at a time. - # So if there are multiple handoffs in the response, only run the first one. - if len(run_handoffs) > 1: - logger.info( - f"{log_prefix} Multiple handoffs detected. Ignoring: {[h.name for h in run_handoffs[1:]]}" - ) - - handoff_command = run_handoffs[0] - next_agent = agents_dict.get(handoff_command.name) - if next_agent is None: - raise AgentError( - f"Agent {handoff_command.name} not found in the list of agents." - ) - - # Start a new agent loop with the new agent - agent = next_agent - ctx.set("agent_name", format_name(agent.name)) - continue - - # Handle output messages - # If there are no output messages, then we just continue the loop - final_output = response.output_text - if final_output != "": - logger.info(f"{log_prefix} Final output message generated.") - return AgentResponse( - agent=agent.name, - messages=session_state.get_new_items(), # type: ignore - final_output=final_output, - ) - except AgentError as e: - logger.warning( - f"{log_prefix} Iteration of agent run failed. Updating state and feeding back error to LLM: {str(e)}" - ) - session_state.add_system_message( - ctx, f"Failed iteration of agent run: {str(e)}" - ) - - -async def generate_tool_schemas(agent, agents_dict) -> list[dict[str, Any]]: - tool_schemas = [tool.tool_schema for tool in agent.tools] - for handoff_agent_name in agent.handoffs: - handoff_agent = agents_dict.get(format_name(handoff_agent_name)) - if handoff_agent is None: - logger.warning( - f"Agent {handoff_agent_name} not found in the list of agents. Ignoring this handoff agent." - ) - tool_schemas.append(handoff_agent.to_tool_schema()) - return tool_schemas - - -async def parse_llm_response( - agents_dict: Dict[str, Agent], - output: List[ResponseOutputItem], - tools: Dict[str, RestateTool], -): - tool_calls = [] - run_handoffs = [] - output_messages = [] - for item in output: - if isinstance(item, ResponseOutputMessage): - output_messages.append(item) - - elif isinstance(item, ResponseFunctionToolCall): - if item.name in agents_dict.keys(): - # Handoffs - run_handoffs.append(item) - else: - # Tool calls - if item.name not in tools.keys(): - # feed error message back to LLM - raise AgentError( - f"Error while parsing LLM response: This agent does not have access to this tool: {item.name}. Use another tool or handoff." - ) - tool = tools[item.name] - tool_calls.append(to_tool_call(tool, item)) - else: - # feed error message back to LLM - raise AgentError( - f"Error while parsing LLM response: This agent cannot handle this output type {type(item)}. Use another tool or handoff.", - ) - - return output_messages, run_handoffs, tool_calls - - -def format_name(name: str) -> str: - return name.replace(" ", "_").lower() - - -def restate_tool(tool_call: Callable[[Any, I], Awaitable[O]]) -> RestateTool: - target_handler = handler_from_callable(tool_call) - service_type = target_handler.service_tag.kind - handler_input = target_handler.handler_io.input_type - if handler_input is None or not hasattr(handler_input, 'annotation') or handler_input.annotation is None: - input_type = to_strict_json_schema(RestateRequest[Empty]) - else: - annotation = handler_input.annotation - input_type = to_strict_json_schema(RestateRequest[annotation]) # type: ignore - description = "" - match service_type: - case "object": - description = ( - f"{VIRTUAL_OBJECT_HANDLER_TOOL_PREFIX} \n{target_handler.description}" - ) - case "workflow": - description = ( - f"{WORKFLOW_HANDLER_TOOL_PREFIX} \n{target_handler.description}" - ) - case "service": - if target_handler.description: - description = target_handler.description - case _: - raise TerminalError( - f"Unknown service type {service_type}. Is this tool a Restate handler?" - ) - - return RestateTool( - service_name=target_handler.service_tag.name, - name=target_handler.name, - description=description, - service_type=service_type, - tool_schema={ - "type": "function", - "name": f"{target_handler.name}", - "description": description, - "parameters": input_type, - "strict": True, - }, - ) - - -def get_input_type_from_handler(handler: Callable[[Any, I], Awaitable[O]]) -> Type[I]: - handler_annotations = getattr(handler, "__annotations__", {}) - # The annotations contain the context type (key "ctx"), request type and return type (key "return"). - # The input type is in the annotations with as key the name of the variable in the function. - # Since this can be anything, we search for the value that is not called "ctx" or "return". - input_type = next( - (v for k, v in handler_annotations.items() if k not in {"ctx", "return"}), Empty - ) - return input_type # type: ignore - - -def to_tool_call(tool: RestateTool, item: ResponseFunctionToolCall) -> ToolCall: - tool_request = json.loads(item.arguments) - - if tool_request.get("req") is None: - input_serialized = bytes({}) - else: - input_serialized = json.dumps(tool_request["req"]).encode() - key = None - if tool.service_type in {"workflow", "object"}: - key = tool_request.get("key") - if key is None: - # feed error message back to LLM - raise AgentError( - f"Service key is required for {tool.service_type} ${tool.service_name} but not provided in the request." - ) - - delay_in_millis = tool_request.get("delay_in_millis") - return ToolCall( - name=item.name, - tool=tool, - key=key, - input_bytes=input_serialized, - delay_in_millis=delay_in_millis, - ) \ No newline at end of file diff --git a/advanced/restate-native-agent/utils/__init__.py b/advanced/restate-native-agent/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/advanced/restate-native-agent/utils/account.py b/advanced/restate-native-agent/utils/account.py deleted file mode 100644 index 80c0dba..0000000 --- a/advanced/restate-native-agent/utils/account.py +++ /dev/null @@ -1,67 +0,0 @@ -import restate - -from utils.utils import generate_loan_overview -from utils.models import ( - TransactionHistory, - CustomerLoanOverview, -) -from utils.utils import generate_transactions - -# Keyed by customer ID -account = restate.VirtualObject("Account") - - -# Keys of the K/V state stored in Restate per account -BALANCE = "balance" -TRANSACTION_HISTORY = "transaction_history" -LOANS = "loans" - - -@account.handler() -async def get_customer_loans(ctx: restate.ObjectContext) -> CustomerLoanOverview: - """ - Get the ongoing loan requests and loan payments for the customer. - - Returns: - CustomerLoanOverview: The overview of the customer's outstanding loans and loan requests. - """ - loans = await ctx.get(LOANS, type_hint=CustomerLoanOverview) - # If there are no loans, generate a demo loan overview - if loans is None: - # Generate a demo loan overview - loans = await ctx.run_typed( - "generate loans", - lambda: generate_loan_overview() - ) - ctx.set(LOANS, loans) - return loans - - -@account.handler() -async def get_balance(ctx: restate.ObjectContext) -> float: - """ - Get the balance of the customer. - - Returns: - float: The balance of the customer. - """ - return await ctx.get(BALANCE, type_hint=float) or 100000.0 - - -@account.handler() -async def get_transaction_history(ctx: restate.ObjectContext) -> TransactionHistory: - """ - Get the transaction history of the customer. - - Returns: - TransactionHistory: The transaction history of the customer. - """ - # If there is no transaction history, generate a demo transaction history - history = await ctx.get(TRANSACTION_HISTORY, type_hint=TransactionHistory) - if history is None: - history = await ctx.run_typed( - "generate transactions", - lambda: generate_transactions() - ) - ctx.set(TRANSACTION_HISTORY, history) - return history diff --git a/advanced/restate-native-agent/utils/models.py b/advanced/restate-native-agent/utils/models.py deleted file mode 100644 index 213b34c..0000000 --- a/advanced/restate-native-agent/utils/models.py +++ /dev/null @@ -1,87 +0,0 @@ -from datetime import datetime - -from pydantic import BaseModel, Field -from restate.serde import PydanticJsonSerde - - -# ------------ TRANSACTION MODELS ------------ - - -class Transaction(BaseModel): - reason: str - amount: float - timestamp: str - timestamp_millis: int = Field( - default_factory=lambda data: int( - datetime.strptime(data["timestamp"], "%Y-%m-%d").timestamp() * 1000 - ) - ) - - -class TransactionHistory(BaseModel): - transactions: list[Transaction] - - -# ------------ LOAN MODELS ------------ - - -class RecurringLoanPayment(BaseModel): - monthly_amount: float - months_left: int - - -class LoanRequest(BaseModel): - """ - A loan request object. - - Attributes: - customer_id (str): The customer ID who requested the loan. - loan_amount (int): The amount of the loan. - loan_duration_months (int): The duration of the loan in months. - """ - - customer_id: str - loan_amount: int - loan_duration_months: int - - -class LoanDecision(BaseModel): - """ - A loan decision object. - - Attributes: - loan_id (str): The ID of the loan. - approved (bool): Whether the loan was approved or not. - reason (str): The reason for the decision. - """ - - approved: bool - reason: str - - -class Loan(BaseModel): - """ - A loan object. - - Attributes: - loan_id (str): The ID of the loan. - loan_request (LoanRequest): The initial loan request. - loan_decision (LoanDecision): Information about whether the loan was approved or not, and the reason. - loan_payment (RecurringLoanPayment): Information about the monthly loan payment and remaining duration. - """ - - loan_id: str - loan_request: LoanRequest - loan_decision: LoanDecision | None = None - loan_payment: RecurringLoanPayment | None = None - - -class CustomerLoanOverview(BaseModel): - """ - The ongoing loan requests and loan payments for the customer. - - Attributes: - loans (Dict[str, Loan]): The list of loans. - """ - - loans: list[Loan] = Field(default=[]) diff --git a/advanced/restate-native-agent/utils/utils.py b/advanced/restate-native-agent/utils/utils.py deleted file mode 100644 index d4215ba..0000000 --- a/advanced/restate-native-agent/utils/utils.py +++ /dev/null @@ -1,97 +0,0 @@ -import random - -from datetime import datetime, timedelta - -from .models import ( - TransactionHistory, - Transaction, - CustomerLoanOverview, - LoanRequest, - Loan, - LoanDecision, - RecurringLoanPayment, -) - -regular_categories = { - "income": ["Salary", "Bonus", "Freelance"], - "loan_payment": ["Loan Repayment", "Mortgage Payment"], - "cash_withdrawal": ["ATM Withdrawal"], - "basic_expense": ["Groceries", "Rent", "Utilities"], - "other": ["Gift", "Miscellaneous"], -} - -high_risk_categories = { - "gambling": ["Casino", "Lottery"], - "payday_loan": ["Payday Loan Repayment"], -} - - -def generate_transactions() -> TransactionHistory: - """ - Used to generate a random transaction history for the customer. - To make the demo more interesting. - """ - transactions = [] - start_date = datetime.now() - timedelta(days=365) - for month in range(12): - # Ensure one salary payment per month - salary_date = start_date + timedelta(days=month * 30) - transactions.append( - Transaction( - reason=f"Salary for month {month}", - amount=4000.0, - timestamp=salary_date.strftime("%Y-%m-%d"), - ) - ) - - for _ in range(10): - category = random.choice(list(regular_categories.keys())) - reason = random.choice(regular_categories[category]) - amount = round(random.uniform(-1000, 1000), 2) - if category == "income": - amount = abs(amount) - else: - amount = -abs(amount) - date = start_date + timedelta(days=random.randint(0, 365)) - transactions.append( - Transaction( - reason=reason, amount=amount, timestamp=date.strftime("%Y-%m-%d") - ) - ) - - if random.random() > 0.5: - # Add a few high-risk transaction - for _ in range(6): - category = random.choice(list(high_risk_categories.keys())) - reason = random.choice(high_risk_categories[category]) - amount = round(random.uniform(-1000, 1000), 2) - date = start_date + timedelta(days=random.randint(0, 365)) - transactions.append( - Transaction( - reason=reason, amount=amount, timestamp=date.strftime("%Y-%m-%d") - ) - ) - - return TransactionHistory(transactions=transactions) - - -def generate_loan_overview() -> CustomerLoanOverview: - return CustomerLoanOverview( - loans=[ - Loan( - loan_id="car_purchase", - loan_request=LoanRequest( - customer_id="customer_123", - loan_amount=10000, - loan_duration_months=12, - ), - loan_decision=LoanDecision( - approved=True, - reason="You have a good credit score. And you do not do risky transactions such as gambling.", - ), - loan_payment=RecurringLoanPayment( - monthly_amount=9856.07, months_left=11 - ), - ) - ] - ) diff --git a/advanced/restate-native-agent/uv.lock b/advanced/restate-native-agent/uv.lock deleted file mode 100644 index 37e75fa..0000000 --- a/advanced/restate-native-agent/uv.lock +++ /dev/null @@ -1,819 +0,0 @@ -version = 1 -revision = 1 -requires-python = ">=3.12" - -[[package]] -name = "agents" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "hypercorn" }, - { name = "openai-agents" }, - { name = "pydantic" }, - { name = "restate-sdk", extra = ["serde"] }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, -] - -[package.metadata] -requires-dist = [ - { name = "hypercorn" }, - { name = "openai-agents", specifier = "==0.3.1" }, - { name = "pydantic", specifier = ">=2.11.9" }, - { name = "restate-sdk", extras = ["serde"], specifier = ">=0.10.1" }, -] - -[package.metadata.requires-dev] -dev = [{ name = "mypy", specifier = ">=1.18.2" }] - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, -] - -[[package]] -name = "anyio" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, -] - -[[package]] -name = "attrs" -version = "25.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, -] - -[[package]] -name = "certifi" -version = "2025.4.26" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655 }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223 }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366 }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104 }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830 }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854 }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670 }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501 }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173 }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822 }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543 }, - { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326 }, - { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008 }, - { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196 }, - { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819 }, - { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350 }, - { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644 }, - { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468 }, - { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187 }, - { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699 }, - { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580 }, - { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366 }, - { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342 }, - { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995 }, - { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640 }, - { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636 }, - { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939 }, - { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580 }, - { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870 }, - { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797 }, - { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 }, - { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 }, - { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175 }, -] - -[[package]] -name = "click" -version = "8.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "dacite" -version = "1.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/55/a0/7ca79796e799a3e782045d29bf052b5cde7439a2bbb17f15ff44f7aacc63/dacite-1.9.2.tar.gz", hash = "sha256:6ccc3b299727c7aa17582f0021f6ae14d5de47c7227932c47fec4cdfefd26f09", size = 22420 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/35/386550fd60316d1e37eccdda609b074113298f23cef5bddb2049823fe666/dacite-1.9.2-py3-none-any.whl", hash = "sha256:053f7c3f5128ca2e9aceb66892b1a3c8936d02c686e707bee96e19deef4bc4a0", size = 16600 }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] - -[[package]] -name = "griffe" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439 }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, -] - -[[package]] -name = "h2" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, -] - -[[package]] -name = "httpx-sse" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054 }, -] - -[[package]] -name = "hypercorn" -version = "0.17.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, - { name = "h2" }, - { name = "priority" }, - { name = "wsproto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/3a/df6c27642e0dcb7aff688ca4be982f0fb5d89f2afd3096dc75347c16140f/hypercorn-0.17.3.tar.gz", hash = "sha256:1b37802ee3ac52d2d85270700d565787ab16cf19e1462ccfa9f089ca17574165", size = 44409 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/3b/dfa13a8d96aa24e40ea74a975a9906cfdc2ab2f4e3b498862a57052f04eb/hypercorn-0.17.3-py3-none-any.whl", hash = "sha256:059215dec34537f9d40a69258d323f56344805efb462959e727152b0aa504547", size = 61742 }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] - -[[package]] -name = "jiter" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, -] - -[[package]] -name = "jsonschema" -version = "4.25.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, -] - -[[package]] -name = "mcp" -version = "1.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/e9/242096400d702924b49f8d202c6ded7efb8841cacba826b5d2e6183aef7b/mcp-1.14.1.tar.gz", hash = "sha256:31c4406182ba15e8f30a513042719c3f0a38c615e76188ee5a736aaa89e20134", size = 454944 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/11/d334fbb7c2aeddd2e762b86d7a619acffae012643a5738e698f975a2a9e2/mcp-1.14.1-py3-none-any.whl", hash = "sha256:3b7a479e8e5cbf5361bdc1da8bc6d500d795dc3aff44b44077a363a7f7e945a4", size = 163809 }, -] - -[[package]] -name = "mypy" -version = "1.18.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273 }, - { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910 }, - { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585 }, - { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562 }, - { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296 }, - { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828 }, - { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728 }, - { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758 }, - { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342 }, - { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709 }, - { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806 }, - { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262 }, - { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775 }, - { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852 }, - { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242 }, - { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683 }, - { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749 }, - { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959 }, - { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, -] - -[[package]] -name = "openai" -version = "1.108.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/7a/3f2fbdf82a22d48405c1872f7c3176a705eee80ff2d2715d29472089171f/openai-1.108.1.tar.gz", hash = "sha256:6648468c1aec4eacfa554001e933a9fa075f57bacfc27588c2e34456cee9fef9", size = 563735 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/87/6ad18ce0e7b910e3706480451df48ff9e0af3b55e5db565adafd68a0706a/openai-1.108.1-py3-none-any.whl", hash = "sha256:952fc027e300b2ac23be92b064eac136a2bc58274cec16f5d2906c361340d59b", size = 948394 }, -] - -[[package]] -name = "openai-agents" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffe" }, - { name = "mcp" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "types-requests" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/d9/2940db8114d8e1e3b8cfa1943432e73721ada20c8dc8514aaee15e858c09/openai_agents-0.3.1.tar.gz", hash = "sha256:c39471f62d859c3ed20f3f69704e830f0d0e2a4e321aa1f21eb924f7a16347a8", size = 1722367 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/34/08564f0285a3d8f425588cb9141b1cd14be2e56e39630f5593de3f4f6558/openai_agents-0.3.1-py3-none-any.whl", hash = "sha256:10a2d68c4d993b395cc5048979748fb9747179c14741a3d07a009efb5e5566e6", size = 193894 }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - -[[package]] -name = "priority" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946 }, -] - -[[package]] -name = "pydantic" -version = "2.11.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855 }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, -] - -[[package]] -name = "pydantic-settings" -version = "2.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, -] - -[[package]] -name = "python-dotenv" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, -] - -[[package]] -name = "python-multipart" -version = "0.0.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543 }, - { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040 }, - { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102 }, - { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700 }, - { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700 }, - { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318 }, - { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714 }, - { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800 }, - { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, -] - -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, -] - -[[package]] -name = "restate-sdk" -version = "0.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/d6/bd1b8f3f4e4e5d3fcf856c4d4ed62760911f29a4f427156b6d4b3cdb2c50/restate_sdk-0.10.1.tar.gz", hash = "sha256:e48b373a36d0449261575b9933479cfd0a05f3bba5956f637a5179cae351196a", size = 69545 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/99/29f5e2edd4148277fe4c301706625e2d8638e91375ab4bb9c19faa351988/restate_sdk-0.10.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3b0e8e51a67c8deee0ff72ae27bfcc4b69878e29971aa55518bd77775a34e473", size = 1810824 }, - { url = "https://files.pythonhosted.org/packages/13/b1/9ffd481ff477f3dc32fa434f6a8f94bf17ccf4a873d0a8177826deae016c/restate_sdk-0.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:153bed90730d8b61ed571d880b595a0c90327893f08bd67d32193c3136a5684f", size = 1746626 }, - { url = "https://files.pythonhosted.org/packages/74/4d/b8fb005844ffaeeb34fc1e3c1fa4bfffae262433f5dbb5879c18ad6b1465/restate_sdk-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5390e7f21df046211bcb3c5381e2ab223bf992ede73353857ead6b76950ed913", size = 1944497 }, - { url = "https://files.pythonhosted.org/packages/31/25/1c00876257ea181976635a359fbbe6984e9f0c06e0d3013d0e770aad08dc/restate_sdk-0.10.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ef8353e838a987490835538da255b306d04c12a3d2b5cc5f5a5f99d1f99d8d15", size = 1973534 }, - { url = "https://files.pythonhosted.org/packages/da/75/f898ad75c404daf518cb2fc887198abae2e9aebb7dfd90d0b96aa8a4485c/restate_sdk-0.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f1036a75af084eea6a78e7703e9e18b9afd3ad1cbbeda4bee06af5d731a86b4f", size = 2166090 }, - { url = "https://files.pythonhosted.org/packages/d0/b6/c66fa630fdd2fa8acd5d9fb8b331e7ddd213718e4f91e9d80f9a68667723/restate_sdk-0.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f4641a5ce170e2b770a6fbc30deeb3ae04d6799a2610a3ef993c72a0e10bca02", size = 2181481 }, - { url = "https://files.pythonhosted.org/packages/e6/99/a09fff4b3206a21cd5309255da3562908c8a61d0e33c70c5246dbc148dac/restate_sdk-0.10.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7bb47c3aa5335d424a59adcfb23e528b6d5a5c61d975e232a3dcb78f7e296edc", size = 1810641 }, - { url = "https://files.pythonhosted.org/packages/d1/d2/72b22932d7bd944cdac079b4140cd3f7cae05f474e432b981ed6f253e779/restate_sdk-0.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90d00b7ddd2112218a33f45929b73a236214f0ecda68747c318a5ed8cbb23eda", size = 1746484 }, - { url = "https://files.pythonhosted.org/packages/20/7f/7f5e58fb922320236ce8def8fdf3ed2f652dd2739228d97a4f8aedb90b2e/restate_sdk-0.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4cc33a74a2d7434c7841a06b1450f9b7acbfc20ea00c34caffa56e4771fd7a8", size = 1944182 }, - { url = "https://files.pythonhosted.org/packages/77/f7/485209051b184368d64da69c53864404f6f095aa9c821211055301d088fc/restate_sdk-0.10.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f6918ff9b9be8c6254bb1df21f1192b085665423d6f433a3950b9340c0aef53f", size = 1973814 }, - { url = "https://files.pythonhosted.org/packages/52/c9/9bc4cde260556661fe2ecf3aad4e01a183a7ab967829f095b043739cd8e3/restate_sdk-0.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aaeeff241660b6bb7a48fcdf6c1540356432daa8e9acef6b62825461b19a3b24", size = 2166771 }, - { url = "https://files.pythonhosted.org/packages/fd/63/a65231d2e000626c68e1bb01802139dd7b29949c5c13e8a9345362800e1f/restate_sdk-0.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:39e80dd7353b2fbea13f63c8ca13d3e3159fc53d41f8efa347cf610269b03081", size = 2181461 }, - { url = "https://files.pythonhosted.org/packages/56/82/4a599396c3e1dd480c714884499a03636aa36e2a149aad7e8e5c1046e5d7/restate_sdk-0.10.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a85bcc932b20293a27330a238e2e3e5570a642bcca3de9efac7b9d8656bf2278", size = 1968970 }, - { url = "https://files.pythonhosted.org/packages/94/ae/0d54c74e93ae3ad19315f26176a3bcbe3bf73a0255fea887ccd0e18e9f56/restate_sdk-0.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f666296ac73fbd1a84a805ce960db7184878569626de09ec7f4ae51cbda35f9", size = 2165378 }, - { url = "https://files.pythonhosted.org/packages/bd/a6/464e1a8a3b1a7cdce71e1b130c561cc10ad8ce5b64d190908074a7204ecf/restate_sdk-0.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:872c5e5302d2a60ad5711d172bbf9f1b6ee50fa5105866469471df3c7bed6d93", size = 2179441 }, - { url = "https://files.pythonhosted.org/packages/dd/82/1180fe0f8e27c9c71f05726093b9feac3d9f2809223beebcc75f17b2000e/restate_sdk-0.10.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a48eebbb1e5849abc59f6824808c89c57b9beb77063a63a3a42a75c9ab2b711", size = 1943143 }, -] - -[package.optional-dependencies] -serde = [ - { name = "dacite" }, - { name = "pydantic" }, -] - -[[package]] -name = "rpds-py" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795 }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121 }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976 }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953 }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915 }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883 }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699 }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713 }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324 }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646 }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137 }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343 }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497 }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790 }, - { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741 }, - { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574 }, - { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051 }, - { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395 }, - { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334 }, - { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691 }, - { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868 }, - { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469 }, - { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125 }, - { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341 }, - { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511 }, - { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736 }, - { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462 }, - { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034 }, - { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392 }, - { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355 }, - { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138 }, - { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247 }, - { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699 }, - { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852 }, - { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582 }, - { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126 }, - { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486 }, - { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832 }, - { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249 }, - { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356 }, - { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300 }, - { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714 }, - { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943 }, - { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472 }, - { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676 }, - { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313 }, - { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080 }, - { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868 }, - { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750 }, - { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688 }, - { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225 }, - { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361 }, - { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493 }, - { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623 }, - { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800 }, - { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943 }, - { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739 }, - { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120 }, - { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944 }, - { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283 }, - { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320 }, - { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760 }, - { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476 }, - { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418 }, - { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771 }, - { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022 }, - { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787 }, - { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538 }, - { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512 }, - { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813 }, - { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385 }, - { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097 }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, -] - -[[package]] -name = "sse-starlette" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297 }, -] - -[[package]] -name = "starlette" -version = "0.48.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46", size = 2652949 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736 }, -] - -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, -] - -[[package]] -name = "types-requests" -version = "2.32.4.20250913" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658 }, -] - -[[package]] -name = "typing-extensions" -version = "4.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, -] - -[[package]] -name = "uvicorn" -version = "0.36.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/5e/f0cd46063a02fd8515f0e880c37d2657845b7306c16ce6c4ffc44afd9036/uvicorn-0.36.0.tar.gz", hash = "sha256:527dc68d77819919d90a6b267be55f0e76704dca829d34aea9480be831a9b9d9", size = 80032 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/06/5cc0542b47c0338c1cb676b348e24a1c29acabc81000bced518231dded6f/uvicorn-0.36.0-py3-none-any.whl", hash = "sha256:6bb4ba67f16024883af8adf13aba3a9919e415358604ce46780d3f9bdc36d731", size = 67675 }, -] - -[[package]] -name = "wsproto" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 }, -] diff --git a/patterns/app/__init__.py b/patterns/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/patterns/app/util/__init__.py b/patterns/app/util/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/patterns/README.md b/python-patterns/README.md similarity index 98% rename from patterns/README.md rename to python-patterns/README.md index e6d01eb..ce32dc4 100644 --- a/patterns/README.md +++ b/python-patterns/README.md @@ -2,10 +2,11 @@ These patterns show how you can use Restate to harden LLM-based routing decisions and tool executions. -They do not implement end-to-end agents, but serve as small self-contained patterns that can be mixed and matched to build more complex workflows. +These small self-contained patterns can be mixed and matched to build more complex agents or workflows. The patterns included here: - [Chaining LLM calls](app/chaining.py): Refine the results by calling the LLM iteratively with its own output. +- - [Parallelizing tool calls](app/parallelization.py): Call multiple tools in parallel and wait for their results in a durable way. Tool calls are retried if they fail, and the results are persisted. - [Dynamic tool routing based on LLM output](app/routing_to_tool.py): Route the execution to different tools based on the LLM's output. Routing decisions are persisted and can be retried. - [Multi-agent routing based on LLM output](app/routing_to_agent.py): Route the execution to specialized agents based on the LLM's output. Routing decisions are persisted and can be retried. diff --git a/patterns/__main__.py b/python-patterns/__main__.py similarity index 100% rename from patterns/__main__.py rename to python-patterns/__main__.py diff --git a/advanced/insurance-claims/app/utils/__init__.py b/python-patterns/app/__init__.py similarity index 100% rename from advanced/insurance-claims/app/utils/__init__.py rename to python-patterns/app/__init__.py diff --git a/patterns/app/agent_with_tool.py b/python-patterns/app/agent_with_tool.py similarity index 100% rename from patterns/app/agent_with_tool.py rename to python-patterns/app/agent_with_tool.py diff --git a/patterns/app/chaining.py b/python-patterns/app/chaining.py similarity index 100% rename from patterns/app/chaining.py rename to python-patterns/app/chaining.py diff --git a/patterns/app/chat.py b/python-patterns/app/chat.py similarity index 100% rename from patterns/app/chat.py rename to python-patterns/app/chat.py diff --git a/patterns/app/evaluator_optimizer.py b/python-patterns/app/evaluator_optimizer.py similarity index 100% rename from patterns/app/evaluator_optimizer.py rename to python-patterns/app/evaluator_optimizer.py diff --git a/patterns/app/human_in_the_loop.py b/python-patterns/app/human_in_the_loop.py similarity index 100% rename from patterns/app/human_in_the_loop.py rename to python-patterns/app/human_in_the_loop.py diff --git a/patterns/app/orchestrator_workers.py b/python-patterns/app/orchestrator_workers.py similarity index 100% rename from patterns/app/orchestrator_workers.py rename to python-patterns/app/orchestrator_workers.py diff --git a/patterns/app/parallel_agents.py b/python-patterns/app/parallel_agents.py similarity index 100% rename from patterns/app/parallel_agents.py rename to python-patterns/app/parallel_agents.py diff --git a/patterns/app/parallel_tools.py b/python-patterns/app/parallel_tools.py similarity index 100% rename from patterns/app/parallel_tools.py rename to python-patterns/app/parallel_tools.py diff --git a/patterns/app/routing_to_agent.py b/python-patterns/app/routing_to_agent.py similarity index 100% rename from patterns/app/routing_to_agent.py rename to python-patterns/app/routing_to_agent.py diff --git a/patterns/app/routing_to_remote_agent.py b/python-patterns/app/routing_to_remote_agent.py similarity index 100% rename from patterns/app/routing_to_remote_agent.py rename to python-patterns/app/routing_to_remote_agent.py diff --git a/patterns/app/routing_to_tool.py b/python-patterns/app/routing_to_tool.py similarity index 100% rename from patterns/app/routing_to_tool.py rename to python-patterns/app/routing_to_tool.py diff --git a/advanced/restate-native-agent/restate_agent/__init__.py b/python-patterns/app/util/__init__.py similarity index 100% rename from advanced/restate-native-agent/restate_agent/__init__.py rename to python-patterns/app/util/__init__.py diff --git a/patterns/app/util/litellm_call.py b/python-patterns/app/util/litellm_call.py similarity index 100% rename from patterns/app/util/litellm_call.py rename to python-patterns/app/util/litellm_call.py diff --git a/patterns/app/util/util.py b/python-patterns/app/util/util.py similarity index 100% rename from patterns/app/util/util.py rename to python-patterns/app/util/util.py diff --git a/patterns/pyproject.toml b/python-patterns/pyproject.toml similarity index 100% rename from patterns/pyproject.toml rename to python-patterns/pyproject.toml diff --git a/patterns/uv.lock b/python-patterns/uv.lock similarity index 100% rename from patterns/uv.lock rename to python-patterns/uv.lock From dd9eb4cc346ebc8d1ff3b4081dfccf2b30ec2959 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 6 Oct 2025 13:55:34 +0200 Subject: [PATCH 4/6] Readmes --- python-patterns/README.md | 118 ++++++++---------- python-patterns/app/agent_with_tool.py | 55 -------- python-patterns/app/chaining.py | 2 +- python-patterns/app/chat.py | 8 +- python-patterns/app/evaluator_optimizer.py | 5 +- python-patterns/app/human_in_the_loop.py | 4 +- python-patterns/app/orchestrator_workers.py | 4 +- python-patterns/app/parallel_agents.py | 6 +- python-patterns/app/parallel_tools.py | 5 + python-patterns/app/routing_to_agent.py | 4 +- .../app/routing_to_remote_agent.py | 5 +- python-patterns/app/routing_to_tool.py | 100 +++++++-------- 12 files changed, 125 insertions(+), 191 deletions(-) delete mode 100644 python-patterns/app/agent_with_tool.py diff --git a/python-patterns/README.md b/python-patterns/README.md index ce32dc4..e71835b 100644 --- a/python-patterns/README.md +++ b/python-patterns/README.md @@ -5,17 +5,16 @@ These patterns show how you can use Restate to harden LLM-based routing decision These small self-contained patterns can be mixed and matched to build more complex agents or workflows. The patterns included here: -- [Chaining LLM calls](app/chaining.py): Refine the results by calling the LLM iteratively with its own output. -- -- [Parallelizing tool calls](app/parallelization.py): Call multiple tools in parallel and wait for their results in a durable way. Tool calls are retried if they fail, and the results are persisted. -- [Dynamic tool routing based on LLM output](app/routing_to_tool.py): Route the execution to different tools based on the LLM's output. Routing decisions are persisted and can be retried. -- [Multi-agent routing based on LLM output](app/routing_to_agent.py): Route the execution to specialized agents based on the LLM's output. Routing decisions are persisted and can be retried. -- [Orchestrator-worker pattern](app/orchestrator_workers.py): A resilient orchestration workflow in which a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and analyzes their results. -- [Evaluator-optimizer pattern](app/evaluator_optimizer.py): Let the LLM generate a response, and ask another LLM to evaluate the response, and let them iterate on it. -- [Human-in-the-loop pattern](app/human_in_the_loop.py): An LLM generates a response, and then a human can review and approve the response before the LLM continues with the next step. -- [Chat sessions](app/chat.py): A chat session where the state is kept across multiple requests, and where a human can provide feedback on the LLM's responses. - -A part of these patterns are based on Anthropic's [agents cookbook](https://github.com/anthropics/anthropic-cookbook/tree/main/patterns/agents). +- [Chaining LLM calls](app/chaining.py): Build fault-tolerant processing pipelines where each step transforms the previous step's output. +- [Tool routing](app/routing_to_tool.py): Automatically route requests to tools based on LLM outputs. +- [Parallel tool execution](app/parallel_tools.py): Execute multiple tools in parallel with durable results that persist across failures. +- [Multi-agent routing](app/routing_to_agent.py): Route requests to specialized agents based on LLM outputs. +- [Remote agent routing](app/routing_to_remote_agent.py): Route requests to remote agents with resilient communication. +- [Parallel agent processing](app/parallel_agents.py): Run multiple, specialized agents in parallel and aggregate their results. +- [Orchestrator-worker pattern](app/orchestrator_workers.py): Break down complex tasks into specialized subtasks and execute them in parallel. +- [Evaluator-optimizer pattern](app/evaluator_optimizer.py): Generate → Evaluate → Improve loop until quality criteria are met. +- [Human-in-the-loop pattern](app/human_in_the_loop.py): Implement resilient human approval steps that suspend execution until feedback is received. +- [Chat sessions](app/chat.py): Long-lived, stateful chat sessions that maintain conversation state across multiple requests. ## Why Restate? @@ -28,29 +27,13 @@ It persists routing decisions, tool execution outcomes, and deterministically re The state can be queried from the outside. Stateful sessions are long-lived and can be resumed at any time. - 🎮 **Task control** - Cancel tasks, query status, re-subscribe to ongoing tasks, and track progress across failures, time, and processes. -These benefits are best portrayed in the following patterns: - -| Pattern | Retries & recovery | Exactly-once execution | Persistent memory | -|-----------------------------|--------------------|------------------------|-------------------| -| Chaining LLM calls | ✅ | ✅ | | -| Parallelizing tool calls | ✅ | ✅ | | -| Dynamic routing | ✅ | ✅ | | -| Orchestrator-worker pattern | ✅ | ✅ | | -| Evaluator-optimizer pattern | ✅ | ✅ | | -| Human-in-the-loop pattern | ✅ | ✅ | | -| Chat sessions | ✅ | ✅ | ✅ | - ## Running the examples -1. Export your OpenAI or Anthrophic API key as an environment variable: +1. Export your OpenAI API key as an environment variable: ```shell export OPENAI_API_KEY=your_openai_api_key ``` - or: - ```shell - export ANTHROPIC_API_KEY=your_anthropic_api_key - ``` 2. [Start the Restate Server](https://docs.restate.dev/develop/local_dev) in a separate shell: ```shell restate-server @@ -67,7 +50,7 @@ These benefits are best portrayed in the following patterns: ### Chaining LLM calls [](app/chaining.py) -Refine the results by calling the LLM iteratively with its own output. +Build fault-tolerant processing pipelines where each step transforms the previous step's output. In the UI (`http://localhost:9070`), click on the `run` handler of the `CallChainingService` to open the playground and send a default request: Chaining LLM calls - UI @@ -76,49 +59,66 @@ You see in the Invocations Tab of the UI how the LLM is called multiple times, a Chaining LLM calls - UI -### Parallelizing tool calls -[](app/parallelization.py) - -Call multiple tools in parallel and wait for their results in a durable way. Tool calls are retried if they fail, and the results are persisted. +### Tool routing +[](app/routing_to_tool.py) -In the UI (`http://localhost:9070`), click on the `analyze_text` handler of the `ParallelizationService` to open the playground and send a default request: -Parallel LLM calls - UI +Automatically route requests to tools based on LLM outputs. The agent keeps calling the LLM and executing tools until a final answer is returned. +In the UI (`http://localhost:9070`), click on the `route` handler of the `ToolRouterService` to open the playground and send a default request: +Dynamic routing LLM calls - UI -You see in the UI how the different tasks are executed in parallel: +In the UI, you can see how the LLM decides to forward the request to the technical support tools, and how the response is processed: -Parallel LLM calls - UI +Dynamic routing based on LLM output - UI -Once all tasks are done, the results are aggregated and returned to the client. +### Parallel tool execution +[](app/parallel_tools.py) -### Dynamic routing to tools based on LLM output -[](app/routing_to_tool.py) +Execute multiple tools in parallel with durable results that persist across failures. -Route the execution to different tools based on the LLM's output. Routing decisions are persisted and can be retried. +In the UI (`http://localhost:9070`), click on the `run` handler of the `ParallelToolAgent` to open the playground and send a default request: +Parallel tool calls - UI -In the UI (`http://localhost:9070`), click on the `route` handler of the `ToolRouterService` to open the playground and send a default request: -Dynamic routing LLM calls - UI +You see in the UI how the different tools are executed in parallel: -In the UI, you can see how the LLM decides to forward the request to the technical support tools, and how the response is processed: +Parallel tool calls - UI -Dynamic routing based on LLM output - UI +Once all tools are done, the results are aggregated and returned to the client. -### Multi-agent routing based on LLM output +### Multi-agent routing [](app/routing_to_agent.py) -Route the execution to specialized agents based on the LLM's output. Routing decisions are persisted and can be retried. +Route requests to specialized agents based on LLM outputs. Routing decisions are persisted and can be retried. In the UI (`http://localhost:9070`), click on the `route` handler of the `AgentRouterService` to open the playground and send a default request: -Dynamic routing LLM calls - UI +Multi-agent routing - UI In the UI, you can see how the LLM decides to forward the request to the specialized support agents, and how the response is processed: -Dynamic routing based on LLM output - UI +Multi-agent routing - UI + +### Remote agent routing +[](app/routing_to_remote_agent.py) + +Route requests to remote agents with resilient communication. +Restate proxies requests to remote agents, persisting routing decisions and results. +In case of failures, Restate retries failed executions. + +### Parallel agent processing +[](app/parallel_agents.py) + +Run multiple, specialized agents in parallel and aggregate their results. If any agent fails, Restate retries only the failed agents while preserving completed results. + +In the UI (`http://localhost:9070`), click on the `analyze_text` handler of the `ParallelAgentsService` to open the playground and send a default request: + +You see in the UI how the different agents are executed in parallel: + +Once all agents are done, the results are aggregated and returned to the client. ### Orchestrator-worker pattern [](app/orchestrator_workers.py) -A resilient orchestration workflow in which a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and analyzes their results. +Break down complex tasks into specialized subtasks and execute them in parallel. If any worker fails, Restate retries only that worker while preserving other completed work. In the UI (`http://localhost:9070`), click on the `process_text` handler of the `Orchestrator` to open the playground and send a default request: Orchestrator LLM calls - UI @@ -129,7 +129,7 @@ In the UI, you can see how the LLM split the task in three parts and how each of ### Evaluator-optimizer pattern [](app/evaluator_optimizer.py) -Let the LLM generate a response, and ask another LLM to evaluate the response, and let them iterate on it. +Generate → Evaluate → Improve loop until quality criteria are met. Restate persists each iteration, resuming from the last completed step on failure. In the UI (`http://localhost:9070`), click on the `improve_until_good` handler of the `EvaluatorOptimizer` to open the playground and send a default request: Evaluator-optimizer pattern - UI @@ -137,17 +137,10 @@ In the UI (`http://localhost:9070`), click on the `improve_until_good` handler o In the UI, you can see how the LLM generates a response, and how the evaluator LLM evaluates it and asks for improvements until the response is satisfactory: Evaluator-optimizer pattern - UI - ### Human-in-the-loop pattern - -An LLM generates a response, and then a human can review and approve the response before the LLM continues with the next step. - -#### Option 1: `run_with_promise` handler [](app/human_in_the_loop.py) -This handler gathers human feedback by blocking the generation-evaluation loop on a Promise that gets resolved with human feedback. - -This is a **Durable Promise**, meaning that the promise can be recovered across processes and time. The Promise is persisted inside Restate. +Implement resilient human approval steps that suspend execution until feedback is received. Durable promises survive crashes and can be recovered across process restarts. In the UI (`http://localhost:9070`), click on the `moderate` handler of the `HumanInTheLoopService` to open the playground and send a default request: Human-in-the-loop pattern - UI @@ -160,13 +153,10 @@ You can see how the feedback gets incorporated in the Invocations tab in the Res Human-in-the-loop pattern - UI - -### Long-lived, stateful Chat sessions +### Chat sessions [](app/chat.py) -A chat session where the state is kept across multiple requests, and where a human can provide feedback on the LLM's responses. - -Restate keeps the state. +Long-lived, stateful chat sessions that maintain conversation state across multiple requests. Sessions survive failures and can be resumed at any time. In the UI (`http://localhost:9070`), click on the `message` handler of the `Chat` service to open the playground and send a default request: diff --git a/python-patterns/app/agent_with_tool.py b/python-patterns/app/agent_with_tool.py deleted file mode 100644 index 846a559..0000000 --- a/python-patterns/app/agent_with_tool.py +++ /dev/null @@ -1,55 +0,0 @@ -import litellm -import restate -from restate import Context - -from app.util.util import get_weather, WeatherRequest - -weather_agent = restate.Service("WeatherAgent") - - -@weather_agent.handler() -async def run(ctx: Context, prompt: str) -> str: - """Main agent loop with tool calling""" - messages = [{"role": "user", "content": prompt}] - - while True: - # Call LLM with durable execution - result = await ctx.run_typed( - "llm-call", - litellm.completion, - model = "gpt-4o", - messages=messages, - tools=[{ - "type": "function", - "function": { - "name": "get_weather", - "description": "Get the current weather in a given location", - "parameters": WeatherRequest.model_json_schema(), - }, - }], - ) - response = result.choices[0].message - messages.append(response) - - # No tool calls, return the response - if not response.tool_calls: - return response.content - - # Sequentially call each tool and add the result to messages - for tool_call in response.tool_calls: - if tool_call.function.name == "get_weather": - tool_output = await ctx.run_typed( - "Get weather", - get_weather, - req=WeatherRequest.model_validate_json( - tool_call.function.arguments - ), - ) - messages.append( - { - "role": "tool", - "tool_call_id": tool_call.id, - "content": tool_output, - } - ) - diff --git a/python-patterns/app/chaining.py b/python-patterns/app/chaining.py index a199412..ceb513d 100644 --- a/python-patterns/app/chaining.py +++ b/python-patterns/app/chaining.py @@ -7,7 +7,7 @@ LLM Prompt Chaining Build fault-tolerant processing pipelines where each step transforms the previous step's output. -If any step fails, Restate automatically resumes from that exact point—no lost work. +If any step fails, Restate automatically resumes from that exact point. Input → Analysis → Extraction → Summary → Result """ diff --git a/python-patterns/app/chat.py b/python-patterns/app/chat.py index ac1e4f2..0ee4fe7 100644 --- a/python-patterns/app/chat.py +++ b/python-patterns/app/chat.py @@ -1,9 +1,15 @@ import restate -from litellm.types.utils import Message from pydantic import BaseModel from .util.litellm_call import llm_call +""" +Long-lived, Stateful Chat Sessions + +Maintains conversation state across multiple requests using Restate's persistent memory. +Sessions survive failures and can be resumed at any time. +""" + chat = restate.VirtualObject("Chat") # Example input text to analyze diff --git a/python-patterns/app/evaluator_optimizer.py b/python-patterns/app/evaluator_optimizer.py index c4e3ddf..f776c29 100644 --- a/python-patterns/app/evaluator_optimizer.py +++ b/python-patterns/app/evaluator_optimizer.py @@ -4,11 +4,10 @@ from .util.util import print_evaluation """ -LLM Iterative Improvement +Evaluator-Optimizer Pattern Generate → Evaluate → Improve loop until quality criteria are met. -Restate persists each iteration—if the process fails after 10 iterations, -it resumes from iteration 10, not from the beginning. +Restate persists each iteration, resuming from the last completed step on failure. Generate → Evaluate → [Pass/Improve] → Final Result """ diff --git a/python-patterns/app/human_in_the_loop.py b/python-patterns/app/human_in_the_loop.py index e7ebc11..308d026 100644 --- a/python-patterns/app/human_in_the_loop.py +++ b/python-patterns/app/human_in_the_loop.py @@ -5,10 +5,10 @@ from .util.util import notify_moderator, Content """ -Human-in-the-loop workflows with Restate +Human-in-the-Loop Pattern Implement resilient human approval steps that suspend execution until feedback is received. -These durable promises survive crashes and can be recovered on other processes on retries. +Durable promises survive crashes and can be recovered across process restarts. """ content_moderator_svc = restate.Service("HumanInTheLoopService") diff --git a/python-patterns/app/orchestrator_workers.py b/python-patterns/app/orchestrator_workers.py index 6f354b2..5f6e7c8 100644 --- a/python-patterns/app/orchestrator_workers.py +++ b/python-patterns/app/orchestrator_workers.py @@ -9,10 +9,10 @@ import litellm """ -LLM Orchestrator-Workers +Orchestrator-Worker Pattern Break down complex tasks into specialized subtasks and execute them in parallel. -If any worker fails, Restate retries only that worker—other completed work is preserved. +If any worker fails, Restate retries only that worker while preserving other completed work. Task → Orchestrator → [Worker A, Worker B, Worker C] → Aggregated Results """ diff --git a/python-patterns/app/parallel_agents.py b/python-patterns/app/parallel_agents.py index e729745..ee3db8d 100644 --- a/python-patterns/app/parallel_agents.py +++ b/python-patterns/app/parallel_agents.py @@ -4,10 +4,10 @@ from .util.litellm_call import llm_call """ -LLM Parallel Processing +Parallel Agent Processing -Process multiple inputs simultaneously with the same prompt. -If any task fails, Restate retries only the failed tasks; completed results are preserved. +Process multiple inputs simultaneously with specialized agents. +If any task fails, Restate retries only the failed tasks while preserving completed results. Task A ↘ Task B → [Wait on Results] → Results A, B, C diff --git a/python-patterns/app/parallel_tools.py b/python-patterns/app/parallel_tools.py index 4fb95ee..fa9fe9a 100644 --- a/python-patterns/app/parallel_tools.py +++ b/python-patterns/app/parallel_tools.py @@ -5,6 +5,11 @@ from app.util.litellm_call import llm_call from app.util.util import get_weather, WeatherRequest +""" +Parallel Tool Execution + +Execute multiple tools in parallel with durable results that persist across failures. +""" parallel_tools_agent = restate.Service("ParallelToolAgent") get_weather_tool= { diff --git a/python-patterns/app/routing_to_agent.py b/python-patterns/app/routing_to_agent.py index fc12e63..2366fed 100644 --- a/python-patterns/app/routing_to_agent.py +++ b/python-patterns/app/routing_to_agent.py @@ -4,10 +4,10 @@ from pydantic import BaseModel """ -LLM Request Routing +Multi-Agent Routing Automatically route requests to specialized agents based on content analysis. -Each route is handled by a dedicated agent service with domain expertise. +Routing decisions are persisted and can be retried if they fail. Request → Classifier → Agent A/B/C → Specialized Response """ diff --git a/python-patterns/app/routing_to_remote_agent.py b/python-patterns/app/routing_to_remote_agent.py index ff4dee9..32f8c59 100644 --- a/python-patterns/app/routing_to_remote_agent.py +++ b/python-patterns/app/routing_to_remote_agent.py @@ -4,10 +4,11 @@ from pydantic import BaseModel """ -LLM Request Routing +Multi-Agent Routing Automatically route requests to specialized agents based on content analysis. -Each route is handled by a dedicated agent service with domain expertise. +Routing decisions are persisted and can be retried if they fail. +Agents can be deployed as separate services, to scale independently. Request → Classifier → Agent A/B/C → Specialized Response """ diff --git a/python-patterns/app/routing_to_tool.py b/python-patterns/app/routing_to_tool.py index a3b1e97..73f0f9d 100644 --- a/python-patterns/app/routing_to_tool.py +++ b/python-patterns/app/routing_to_tool.py @@ -12,12 +12,11 @@ ) """ -LLM Request Routing to Tools +Dynamic Tool Routing -Automatically route customer support requests to specialized backend tools. -Tools handle database queries, service status checks, and ticket management. - -Support Request → Classifier → Database/API/CRM Tool → Operational Result +Implement a custom agent loop that calls external, specialized tools based on LLM instructions. +All steps are durable and recoverable. +The agent keeps calling the LLM and executing tools until a final answer is returned. """ @@ -73,57 +72,46 @@ class Prompt(BaseModel): @tool_router_service.handler() async def route(ctx: restate.Context, prompt: Prompt) -> str: - """Classify request and route to appropriate tool function.""" + """Customer support for questions about account, billing, service status, and issues""" messages = [{"role": "user", "content": prompt.message}] - # Classify the customer support request - result = await ctx.run_typed( - "LLM call", - llm_call, - RunOptions(max_attempts=3, type_hint=Message), - messages=messages, - tools=[create_ticket_tool, service_status_tool, user_database_tool], - ) - messages.append(result) - - if not result.tool_calls: - return result.content - - for tool_call in result.tool_calls: - fn = tool_call.function - # Route to appropriate support tool - if fn.name == "query_user_database": - tool_result = await ctx.run_typed( - "Query user DB", query_user_db, user_id=prompt.user_id - ) - elif fn.name == "fetch_service_status": - tool_result = await ctx.run_typed( - "Get service status", fetch_service_status - ) - elif fn.name == "create_ticket": - tool_result = await ctx.run_typed( - "create support ticket", - create_support_ticket, - ticket=SupportTicket.model_validate_json(fn.arguments), - ) - else: - tool_result = f"Didn't find tool for {fn.name}" - messages.append( - { - "tool_call_id": tool_call.id, - "role": "tool", - "name": fn.name, - "content": tool_result, - } + while True: + result = await ctx.run_typed( + "LLM call", + llm_call, + RunOptions(max_attempts=3, type_hint=Message), + messages=messages, + tools=[create_ticket_tool, service_status_tool, user_database_tool], ) - - # Final response to user based on tool result - response = await ctx.run_typed( - "analyze tool output", - llm_call, - RunOptions(max_attempts=3, type_hint=Message), - prompt=f"Provide a concise, friendly response to the user question {prompt.message} based on the tool output.", - messages=messages, - ) - - return response.content + messages.append(result) + + if not result.tool_calls: + return result.content + + for tool_call in result.tool_calls: + fn = tool_call.function + # Route to appropriate support tool + if fn.name == "query_user_database": + tool_result = await ctx.run_typed( + "Query user DB", query_user_db, user_id=prompt.user_id + ) + elif fn.name == "fetch_service_status": + tool_result = await ctx.run_typed( + "Get service status", fetch_service_status + ) + elif fn.name == "create_ticket": + tool_result = await ctx.run_typed( + "create support ticket", + create_support_ticket, + ticket=SupportTicket.model_validate_json(fn.arguments), + ) + else: + tool_result = f"Didn't find tool for {fn.name}" + messages.append( + { + "tool_call_id": tool_call.id, + "role": "tool", + "name": fn.name, + "content": tool_result, + } + ) From 4a0d9b636a25316f585f4c3e3bbc9bd8bc590d0d Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 6 Oct 2025 14:06:15 +0200 Subject: [PATCH 5/6] Readmes --- README.md | 30 ++++++++++++------------------ python-patterns/README.md | 2 +- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5f115fc..08cbc78 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The Restate approach works **independent of specific SDKs** but **integrates eas Restate is a flexible general-purpose runtime for what we call _innately resilient application_. It is not limited to agentic workflow use cases and is being used for a variety of other use cases as well, including financial transactions or order processing. These examples show how to build agents directly on Restate's durable execution and state management: -- [Standalone Agent](advanced/restate-native-agent/) +- **[ Python Patterns](python-patterns/README.md)** for hardening custom LLM orchestration logic. ## Use Cases @@ -59,22 +59,19 @@ Restate is a flexible general-purpose runtime for what we call _innately resilie 2. [**Restate + OpenAI Agents Python SDK**](openai-agents): - [ Template](openai-agents/template): A minimal example of how to use Restate with the OpenAI Agents SDK. - [ Examples](openai-agents/examples): A more advanced example of how to use Restate with the OpenAI Agents SDK. -2. [**Patterns**](patterns) for hardening custom LLM orchestration logic. - - [ Chaining LLM calls](patterns#chaining-llm-calls) - - [ Parallelizing tool calls](patterns#parallelizing-tool-calls) - - [ Dynamic tool routing based on LLM output](patterns#dynamic-routing-to-tools-based-on-llm-output) - - [ Multi-agent routing based on LLM output](patterns#multi-agent-routing-based-on-llm-output) - - [ Orchestrator-worker](patterns#orchestrator-worker-pattern) - - [ Evaluator-optimizer](patterns#evaluator-optimizer-pattern) - - [ Human-in-the-loop](patterns#human-in-the-loop-pattern) - - [ Chat sessions](patterns#long-lived-stateful-chat-sessions) +2. [**Restate + any AI SDK**](python-patterns): patterns for hardening custom LLM orchestration logic. + - [ Chaining LLM calls](python-patterns/app/chaining.py): Build fault-tolerant processing pipelines where each step transforms the previous step's output. + - [ Tool routing](python-patterns/app/routing_to_tool.py): Automatically route requests to tools based on LLM outputs. + - [ Parallel tool execution](python-patterns/app/parallel_tools.py): Execute multiple tools in parallel with durable results that persist across failures. + - [ Multi-agent routing](python-patterns/app/routing_to_agent.py): Route requests to specialized agents based on LLM outputs. + - [ Remote agent routing](python-patterns/app/routing_to_remote_agent.py): Route requests to remote agents with resilient communication. + - [ Parallel agent processing](python-patterns/app/parallel_agents.py): Run multiple, specialized agents in parallel and aggregate their results. + - [ Orchestrator-worker pattern](python-patterns/app/orchestrator_workers.py): Break down complex tasks into specialized subtasks and execute them in parallel. + - [ Evaluator-optimizer pattern](python-patterns/app/evaluator_optimizer.py): Generate → Evaluate → Improve loop until quality criteria are met. + - [ Human-in-the-loop pattern](python-patterns/app/human_in_the_loop.py): Implement resilient human approval steps that suspend execution until feedback is received. + - [ Chat sessions](python-patterns/app/chat.py): Long-lived, stateful chat sessions that maintain conversation state across multiple requests. 3. [**MCP** ](mcp): Using Restate for exposing tools and resilient orchestration of tool calls. 4. [**A2A** ](a2a): Implement Google's Agent-to-Agent protocol with Restate as resilient, scalable task orchestrator. -5. [**Advanced examples**](end-to-end-applications): - - [ Restate-native agent](advanced/restate-native-agent/README.md): A fully customizable agent implemented with Restate without an Agent SDK. - - [ Interruptible agents](advanced/interruptible-agent/README.md): A customized agent with different operational modes to process new inputs: interrupting, incorporating, queueing. - - [ Insurance claims](advanced/insurance-claims/README.md): Filing insurance claims by parsing PDF receipts with LLMs. - Restate currently supports 6 languages: @@ -98,8 +95,5 @@ Join our [Discord](https://discord.gg/skW3AZ6uGd)/[Slack](https://join.slack.com ## Acknowledgements -- The implementations of the Restate-native agent in this repo are heavily inspired by the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python). We therefore want to give credit to the developers of this SDK for the great work they have done. This repo builds further on their work to make it benefit from Restate's programming model and capabilities. - - The DIY patterns are largely based on Anthropic's [agents cookbook](https://github.com/anthropics/anthropic-cookbook/tree/main/patterns/agents). - - Some of the A2A examples in this repo are based on the examples included in the [Google A2A repo](https://github.com/google/A2A/tree/main). diff --git a/python-patterns/README.md b/python-patterns/README.md index e71835b..c542d75 100644 --- a/python-patterns/README.md +++ b/python-patterns/README.md @@ -111,7 +111,7 @@ Run multiple, specialized agents in parallel and aggregate their results. If any In the UI (`http://localhost:9070`), click on the `analyze_text` handler of the `ParallelAgentsService` to open the playground and send a default request: -You see in the UI how the different agents are executed in parallel: +You see in the UI how the different agents are executed in parallel. Once all agents are done, the results are aggregated and returned to the client. From bafc594a6ce5467f45c853fd1b1ba94a1968ce17 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 6 Oct 2025 14:13:42 +0200 Subject: [PATCH 6/6] Make code snippets more Concise --- python-patterns/app/chaining.py | 7 ++++--- python-patterns/app/chat.py | 3 ++- python-patterns/app/evaluator_optimizer.py | 6 ++++-- python-patterns/app/orchestrator_workers.py | 2 +- python-patterns/app/parallel_agents.py | 7 ++++--- python-patterns/app/routing_to_agent.py | 9 +++++---- python-patterns/app/routing_to_remote_agent.py | 11 +++++++---- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/python-patterns/app/chaining.py b/python-patterns/app/chaining.py index ceb513d..5af20f7 100644 --- a/python-patterns/app/chaining.py +++ b/python-patterns/app/chaining.py @@ -1,5 +1,6 @@ import restate from pydantic import BaseModel +from restate import RunOptions from .util.litellm_call import llm_call @@ -32,7 +33,7 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: result = await ctx.run_typed( "Extract metrics", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Extract only the numerical values and their associated metrics from the text. " f"Format each as 'metric name: metric' on a new line. Input: {prompt.message}", ) @@ -41,7 +42,7 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: result2 = await ctx.run_typed( "Sort metrics", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Sort all lines in descending order by numerical value. Input: {result}", ) @@ -49,7 +50,7 @@ async def run(ctx: restate.Context, prompt: Prompt) -> str: result3 = await ctx.run_typed( "Format as table", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Format the sorted data as a markdown table with columns 'Metric Name' and 'Value'. Input: {result2}", ) diff --git a/python-patterns/app/chat.py b/python-patterns/app/chat.py index 0ee4fe7..464ff8f 100644 --- a/python-patterns/app/chat.py +++ b/python-patterns/app/chat.py @@ -1,5 +1,6 @@ import restate from pydantic import BaseModel +from restate import RunOptions from .util.litellm_call import llm_call @@ -31,7 +32,7 @@ async def message(ctx: restate.ObjectContext, prompt: Prompt) -> str: result = await ctx.run_typed( "LLM call", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), messages=memory, ) diff --git a/python-patterns/app/evaluator_optimizer.py b/python-patterns/app/evaluator_optimizer.py index f776c29..8c3df26 100644 --- a/python-patterns/app/evaluator_optimizer.py +++ b/python-patterns/app/evaluator_optimizer.py @@ -1,5 +1,7 @@ import restate from pydantic import BaseModel +from restate import RunOptions + from .util.litellm_call import llm_call from .util.util import print_evaluation @@ -37,7 +39,7 @@ async def improve_until_good(ctx: restate.Context, prompt: Prompt) -> str: solution_response = await ctx.run_typed( f"generate_v{iteration+1}", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system="Create a Python function to solve this task. Eagerly return results for review.", prompt=f" Previous attempts: {attempts} - Task: {prompt}" "", ) @@ -48,7 +50,7 @@ async def improve_until_good(ctx: restate.Context, prompt: Prompt) -> str: evaluation_response = await ctx.run_typed( f"evaluate_v{iteration+1}", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system=f"""Evaluate this solution on correctness, efficiency, and readability. Reply with either: 'PASS: [brief reason]' if the solution is correct and very well-implemented diff --git a/python-patterns/app/orchestrator_workers.py b/python-patterns/app/orchestrator_workers.py index 5f6e7c8..d5f3cf5 100644 --- a/python-patterns/app/orchestrator_workers.py +++ b/python-patterns/app/orchestrator_workers.py @@ -68,7 +68,7 @@ async def process_text(ctx: restate.Context, prompt: Prompt) -> list[str]: worker_task = ctx.run_typed( task.task_type, llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system=f"You are a {task.task_type} specialist.", prompt=f"Task: {task.instruction} - Text to analyze: {prompt}", ) diff --git a/python-patterns/app/parallel_agents.py b/python-patterns/app/parallel_agents.py index ee3db8d..62ca769 100644 --- a/python-patterns/app/parallel_agents.py +++ b/python-patterns/app/parallel_agents.py @@ -1,5 +1,6 @@ import restate from pydantic import BaseModel +from restate import RunOptions from .util.litellm_call import llm_call @@ -37,21 +38,21 @@ async def analyze_text(ctx: restate.Context, prompt: Prompt) -> list[str]: sentiment_task = ctx.run_typed( "Analyze sentiment", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Analyze sentiment (positive/negative/neutral): {prompt}", ) key_points_task = ctx.run_typed( "Extract key points", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Extract 3 key points as bullets: {prompt}", ) summary_task = ctx.run_typed( "Summarize", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=f"Summarize in one sentence: {prompt}", ) diff --git a/python-patterns/app/routing_to_agent.py b/python-patterns/app/routing_to_agent.py index 2366fed..57b9dbb 100644 --- a/python-patterns/app/routing_to_agent.py +++ b/python-patterns/app/routing_to_agent.py @@ -1,4 +1,5 @@ import restate +from restate import RunOptions from .util.litellm_call import llm_call from pydantic import BaseModel @@ -64,7 +65,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: result = await ctx.run_typed( "handle request", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=prompt.message, tools=[billing_agent, account_agent, product_agent], ) @@ -79,7 +80,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: result =await ctx.run_typed( "run billing agent", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system="You are a billing support specialist." "Acknowledge the billing issue, explain charges clearly, provide next steps with timeline." "Keep responses professional but friendly.", @@ -90,7 +91,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: result = await ctx.run_typed( "run account agent", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system="You are an account security specialist." "Prioritize account security and verification, provide clear recovery steps, include security tips." "Maintain a serious, security-focused tone.", @@ -101,7 +102,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: result= await ctx.run_typed( "run product agent", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system="You are a product specialist." "Focus on feature education and best practices, include specific examples, suggest related features." "Be educational and encouraging in tone.", diff --git a/python-patterns/app/routing_to_remote_agent.py b/python-patterns/app/routing_to_remote_agent.py index 32f8c59..360a4f0 100644 --- a/python-patterns/app/routing_to_remote_agent.py +++ b/python-patterns/app/routing_to_remote_agent.py @@ -1,5 +1,8 @@ import restate import json + +from restate import RunOptions + from .util.litellm_call import llm_call from pydantic import BaseModel @@ -40,7 +43,7 @@ async def route(ctx: restate.Context, prompt: Prompt) -> str: result = await ctx.run_typed( "handle request", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), prompt=prompt.message, tools=[billing_agent, account_agent, product_agent], ) @@ -80,7 +83,7 @@ async def get_billing_support(ctx: restate.Context, prompt: str) -> str: result = await ctx.run_typed( "billing_response", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system=f"""You are a billing support specialist. Acknowledge the billing issue, explain charges clearly, provide next steps with timeline. Keep responses professional but friendly.""", @@ -106,7 +109,7 @@ async def get_account_support(ctx: restate.Context, prompt: str) -> str: result = await ctx.run_typed( "account_response", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system=f"""You are an account security specialist. Prioritize account security and verification, provide clear recovery steps, include security tips. Maintain a serious, security-focused tone.""", @@ -132,7 +135,7 @@ async def get_product_support(ctx: restate.Context, prompt: str) -> str: result = await ctx.run_typed( "product_response", llm_call, - restate.RunOptions(max_attempts=3), + RunOptions(max_attempts=3), system=f"""You are a product specialist. Focus on feature education and best practices, include specific examples, suggest related features. Be educational and encouraging in tone.""",