From b9dd166a6b1697e7bb61bf181d6812693ddbc19f Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Sun, 28 Dec 2025 10:21:32 -0800 Subject: [PATCH 1/3] Lorenze/agent executor flow pattern (#3975) * WIP gh pr refactor: update agent executor handling and introduce flow-based executor * wip * refactor: clean up comments and improve code clarity in agent executor flow - Removed outdated comments and unnecessary explanations in and classes to enhance code readability. - Simplified parameter updates in the agent executor to avoid confusion regarding executor recreation. - Improved clarity in the method to ensure proper handling of non-final answers without raising errors. * bumping pytest-randomly numpy * also bump versions of anthropic sdk * ensure flow logs are not passed if its on executor * revert anthropic bump * fix * refactor: update dependency markers in uv.lock for platform compatibility - Enhanced dependency markers for , , , and others to ensure compatibility across different platforms (Linux, Darwin, and architecture-specific conditions). - Removed unnecessary event emission in the class during kickoff. - Cleaned up commented-out code in the class for better readability and maintainability. * drop dupllicate * test: enhance agent executor creation and stop word assertions - Added calls to create_agent_executor in multiple test cases to ensure proper agent execution setup. - Updated assertions for stop words in the agent tests to remove unnecessary checks and improve clarity. - Ensured consistency in task handling by invoking create_agent_executor with the appropriate task parameter. * refactor: reorganize agent executor imports and introduce CrewAgentExecutorFlow - Removed the old import of CrewAgentExecutorFlow and replaced it with the new import from the experimental module. - Updated relevant references in the codebase to ensure compatibility with the new structure. - Enhanced the organization of imports in core.py and base_agent.py for better clarity and maintainability. * updating name * dropped usage of printer here for rich console and dropped non-added value logging * address i18n * Enhance concurrency control in CrewAgentExecutorFlow by introducing a threading lock to prevent concurrent executions. This change ensures that the executor instance cannot be invoked while already running, improving stability and reliability during flow execution. * string literal returns * string literal returns * Enhance CrewAgentExecutor initialization by allowing optional i18n parameter for improved internationalization support. This change ensures that the executor can utilize a provided i18n instance or fallback to the default, enhancing flexibility in multilingual contexts. --------- Co-authored-by: Greyson LaLonde --- lib/crewai/src/crewai/agent/core.py | 105 +- .../crewai/agents/agent_builder/base_agent.py | 2 - .../src/crewai/agents/crew_agent_executor.py | 3 +- .../src/crewai/experimental/__init__.py | 2 + .../experimental/crew_agent_executor_flow.py | 808 +++++++++++++++ .../evaluation/agent_evaluator.py | 9 +- .../experimental/evaluation/base_evaluator.py | 9 +- .../evaluation/experiment/runner.py | 10 +- .../evaluation/metrics/goal_metrics.py | 9 +- .../evaluation/metrics/reasoning_metrics.py | 9 +- .../metrics/semantic_quality_metrics.py | 9 +- .../evaluation/metrics/tools_metrics.py | 11 +- lib/crewai/src/crewai/flow/flow.py | 150 +-- lib/crewai/tests/agents/test_agent.py | 12 +- .../agents/test_crew_agent_executor_flow.py | 479 +++++++++ uv.lock | 978 +++++++++--------- 16 files changed, 2010 insertions(+), 595 deletions(-) create mode 100644 lib/crewai/src/crewai/experimental/crew_agent_executor_flow.py create mode 100644 lib/crewai/tests/agents/test_crew_agent_executor_flow.py diff --git a/lib/crewai/src/crewai/agent/core.py b/lib/crewai/src/crewai/agent/core.py index f59724343..3658a330b 100644 --- a/lib/crewai/src/crewai/agent/core.py +++ b/lib/crewai/src/crewai/agent/core.py @@ -1,7 +1,7 @@ from __future__ import annotations import asyncio -from collections.abc import Sequence +from collections.abc import Callable, Sequence import shutil import subprocess import time @@ -44,6 +44,7 @@ from crewai.events.types.memory_events import ( MemoryRetrievalCompletedEvent, MemoryRetrievalStartedEvent, ) +from crewai.experimental.crew_agent_executor_flow import CrewAgentExecutorFlow from crewai.knowledge.knowledge import Knowledge from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource from crewai.lite_agent import LiteAgent @@ -105,7 +106,7 @@ class Agent(BaseAgent): The agent can also have memory, can operate in verbose mode, and can delegate tasks to other agents. Attributes: - agent_executor: An instance of the CrewAgentExecutor class. + agent_executor: An instance of the CrewAgentExecutor or CrewAgentExecutorFlow class. role: The role of the agent. goal: The objective of the agent. backstory: The backstory of the agent. @@ -221,6 +222,10 @@ class Agent(BaseAgent): default=None, description="A2A (Agent-to-Agent) configuration for delegating tasks to remote agents. Can be a single A2AConfig or a dict mapping agent IDs to configs.", ) + executor_class: type[CrewAgentExecutor] | type[CrewAgentExecutorFlow] = Field( + default=CrewAgentExecutor, + description="Class to use for the agent executor. Defaults to CrewAgentExecutor, can optionally use CrewAgentExecutorFlow.", + ) @model_validator(mode="before") def validate_from_repository(cls, v: Any) -> dict[str, Any] | None | Any: # noqa: N805 @@ -721,29 +726,83 @@ class Agent(BaseAgent): self.response_template.split("{{ .Response }}")[1].strip() ) - self.agent_executor = CrewAgentExecutor( - llm=self.llm, # type: ignore[arg-type] - task=task, # type: ignore[arg-type] - agent=self, - crew=self.crew, - tools=parsed_tools, - prompt=prompt, - original_tools=raw_tools, - stop_words=stop_words, - max_iter=self.max_iter, - tools_handler=self.tools_handler, - tools_names=get_tool_names(parsed_tools), - tools_description=render_text_description_and_args(parsed_tools), - step_callback=self.step_callback, - function_calling_llm=self.function_calling_llm, - respect_context_window=self.respect_context_window, - request_within_rpm_limit=( - self._rpm_controller.check_or_wait if self._rpm_controller else None - ), - callbacks=[TokenCalcHandler(self._token_process)], - response_model=task.response_model if task else None, + rpm_limit_fn = ( + self._rpm_controller.check_or_wait if self._rpm_controller else None ) + if self.agent_executor is not None: + self._update_executor_parameters( + task=task, + tools=parsed_tools, + raw_tools=raw_tools, + prompt=prompt, + stop_words=stop_words, + rpm_limit_fn=rpm_limit_fn, + ) + else: + self.agent_executor = self.executor_class( + llm=cast(BaseLLM, self.llm), + task=task, + i18n=self.i18n, + agent=self, + crew=self.crew, + tools=parsed_tools, + prompt=prompt, + original_tools=raw_tools, + stop_words=stop_words, + max_iter=self.max_iter, + tools_handler=self.tools_handler, + tools_names=get_tool_names(parsed_tools), + tools_description=render_text_description_and_args(parsed_tools), + step_callback=self.step_callback, + function_calling_llm=self.function_calling_llm, + respect_context_window=self.respect_context_window, + request_within_rpm_limit=rpm_limit_fn, + callbacks=[TokenCalcHandler(self._token_process)], + response_model=task.response_model if task else None, + ) + + def _update_executor_parameters( + self, + task: Task | None, + tools: list, + raw_tools: list[BaseTool], + prompt: dict, + stop_words: list[str], + rpm_limit_fn: Callable | None, + ) -> None: + """Update executor parameters without recreating instance. + + Args: + task: Task to execute. + tools: Parsed tools. + raw_tools: Original tools. + prompt: Generated prompt. + stop_words: Stop words list. + rpm_limit_fn: RPM limit callback function. + """ + self.agent_executor.task = task + self.agent_executor.tools = tools + self.agent_executor.original_tools = raw_tools + self.agent_executor.prompt = prompt + self.agent_executor.stop = stop_words + self.agent_executor.tools_names = get_tool_names(tools) + self.agent_executor.tools_description = render_text_description_and_args(tools) + self.agent_executor.response_model = task.response_model if task else None + + self.agent_executor.tools_handler = self.tools_handler + self.agent_executor.request_within_rpm_limit = rpm_limit_fn + + if self.agent_executor.llm: + existing_stop = getattr(self.agent_executor.llm, "stop", []) + self.agent_executor.llm.stop = list( + set( + existing_stop + stop_words + if isinstance(existing_stop, list) + else stop_words + ) + ) + def get_delegation_tools(self, agents: list[BaseAgent]) -> list[BaseTool]: agent_tools = AgentTools(agents=agents) return agent_tools.tools() diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py index 6a3262bfb..93907c7c1 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py @@ -457,7 +457,6 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta): if self.cache: self.cache_handler = cache_handler self.tools_handler.cache = cache_handler - self.create_agent_executor() def set_rpm_controller(self, rpm_controller: RPMController) -> None: """Set the rpm controller for the agent. @@ -467,7 +466,6 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta): """ if not self._rpm_controller: self._rpm_controller = rpm_controller - self.create_agent_executor() def set_knowledge(self, crew_embedder: EmbedderConfig | None = None) -> None: pass diff --git a/lib/crewai/src/crewai/agents/crew_agent_executor.py b/lib/crewai/src/crewai/agents/crew_agent_executor.py index 580119a99..e11e3e80a 100644 --- a/lib/crewai/src/crewai/agents/crew_agent_executor.py +++ b/lib/crewai/src/crewai/agents/crew_agent_executor.py @@ -91,6 +91,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): request_within_rpm_limit: Callable[[], bool] | None = None, callbacks: list[Any] | None = None, response_model: type[BaseModel] | None = None, + i18n: I18N | None = None, ) -> None: """Initialize executor. @@ -114,7 +115,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): callbacks: Optional callbacks list. response_model: Optional Pydantic model for structured outputs. """ - self._i18n: I18N = get_i18n() + self._i18n: I18N = i18n or get_i18n() self.llm = llm self.task = task self.agent = agent diff --git a/lib/crewai/src/crewai/experimental/__init__.py b/lib/crewai/src/crewai/experimental/__init__.py index 09e34820d..9507095ff 100644 --- a/lib/crewai/src/crewai/experimental/__init__.py +++ b/lib/crewai/src/crewai/experimental/__init__.py @@ -1,3 +1,4 @@ +from crewai.experimental.crew_agent_executor_flow import CrewAgentExecutorFlow from crewai.experimental.evaluation import ( AgentEvaluationResult, AgentEvaluator, @@ -23,6 +24,7 @@ __all__ = [ "AgentEvaluationResult", "AgentEvaluator", "BaseEvaluator", + "CrewAgentExecutorFlow", "EvaluationScore", "EvaluationTraceCallback", "ExperimentResult", diff --git a/lib/crewai/src/crewai/experimental/crew_agent_executor_flow.py b/lib/crewai/src/crewai/experimental/crew_agent_executor_flow.py new file mode 100644 index 000000000..7111c97ab --- /dev/null +++ b/lib/crewai/src/crewai/experimental/crew_agent_executor_flow.py @@ -0,0 +1,808 @@ +from __future__ import annotations + +from collections.abc import Callable +import threading +from typing import TYPE_CHECKING, Any, Literal, cast +from uuid import uuid4 + +from pydantic import BaseModel, Field, GetCoreSchemaHandler +from pydantic_core import CoreSchema, core_schema +from rich.console import Console +from rich.text import Text + +from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin +from crewai.agents.parser import ( + AgentAction, + AgentFinish, + OutputParserError, +) +from crewai.events.event_bus import crewai_event_bus +from crewai.events.types.logging_events import ( + AgentLogsExecutionEvent, + AgentLogsStartedEvent, +) +from crewai.flow.flow import Flow, listen, or_, router, start +from crewai.hooks.llm_hooks import ( + get_after_llm_call_hooks, + get_before_llm_call_hooks, +) +from crewai.utilities.agent_utils import ( + enforce_rpm_limit, + format_message_for_llm, + get_llm_response, + handle_agent_action_core, + handle_context_length, + handle_max_iterations_exceeded, + handle_output_parser_exception, + handle_unknown_error, + has_reached_max_iterations, + is_context_length_exceeded, + process_llm_response, +) +from crewai.utilities.constants import TRAINING_DATA_FILE +from crewai.utilities.i18n import I18N, get_i18n +from crewai.utilities.printer import Printer +from crewai.utilities.tool_utils import execute_tool_and_check_finality +from crewai.utilities.training_handler import CrewTrainingHandler +from crewai.utilities.types import LLMMessage + + +if TYPE_CHECKING: + from crewai.agent import Agent + from crewai.agents.tools_handler import ToolsHandler + from crewai.crew import Crew + from crewai.llms.base_llm import BaseLLM + from crewai.task import Task + from crewai.tools.base_tool import BaseTool + from crewai.tools.structured_tool import CrewStructuredTool + from crewai.tools.tool_types import ToolResult + from crewai.utilities.prompts import StandardPromptResult, SystemPromptResult + + +class AgentReActState(BaseModel): + """Structured state for agent ReAct flow execution. + + Replaces scattered instance variables with validated immutable state. + Maps to: self.messages, self.iterations, formatted_answer in current executor. + """ + + messages: list[LLMMessage] = Field(default_factory=list) + iterations: int = Field(default=0) + current_answer: AgentAction | AgentFinish | None = Field(default=None) + is_finished: bool = Field(default=False) + ask_for_human_input: bool = Field(default=False) + + +class CrewAgentExecutorFlow(Flow[AgentReActState], CrewAgentExecutorMixin): + """Flow-based executor matching CrewAgentExecutor interface. + + Inherits from: + - Flow[AgentReActState]: Provides flow orchestration capabilities + - CrewAgentExecutorMixin: Provides memory methods (short/long/external term) + + Note: Multiple instances may be created during agent initialization + (cache setup, RPM controller setup, etc.) but only the final instance + should execute tasks via invoke(). + """ + + def __init__( + self, + llm: BaseLLM, + task: Task, + crew: Crew, + agent: Agent, + prompt: SystemPromptResult | StandardPromptResult, + max_iter: int, + tools: list[CrewStructuredTool], + tools_names: str, + stop_words: list[str], + tools_description: str, + tools_handler: ToolsHandler, + step_callback: Any = None, + original_tools: list[BaseTool] | None = None, + function_calling_llm: BaseLLM | Any | None = None, + respect_context_window: bool = False, + request_within_rpm_limit: Callable[[], bool] | None = None, + callbacks: list[Any] | None = None, + response_model: type[BaseModel] | None = None, + i18n: I18N | None = None, + ) -> None: + """Initialize the flow-based agent executor. + + Args: + llm: Language model instance. + task: Task to execute. + crew: Crew instance. + agent: Agent to execute. + prompt: Prompt templates. + max_iter: Maximum iterations. + tools: Available tools. + tools_names: Tool names string. + stop_words: Stop word list. + tools_description: Tool descriptions. + tools_handler: Tool handler instance. + step_callback: Optional step callback. + original_tools: Original tool list. + function_calling_llm: Optional function calling LLM. + respect_context_window: Respect context limits. + request_within_rpm_limit: RPM limit check function. + callbacks: Optional callbacks list. + response_model: Optional Pydantic model for structured outputs. + """ + self._i18n: I18N = i18n or get_i18n() + self.llm = llm + self.task = task + self.agent = agent + self.crew = crew + self.prompt = prompt + self.tools = tools + self.tools_names = tools_names + self.stop = stop_words + self.max_iter = max_iter + self.callbacks = callbacks or [] + self._printer: Printer = Printer() + self.tools_handler = tools_handler + self.original_tools = original_tools or [] + self.step_callback = step_callback + self.tools_description = tools_description + self.function_calling_llm = function_calling_llm + self.respect_context_window = respect_context_window + self.request_within_rpm_limit = request_within_rpm_limit + self.response_model = response_model + self.log_error_after = 3 + self._console: Console = Console() + + # Error context storage for recovery + self._last_parser_error: OutputParserError | None = None + self._last_context_error: Exception | None = None + + # Execution guard to prevent concurrent/duplicate executions + self._execution_lock = threading.Lock() + self._is_executing: bool = False + self._has_been_invoked: bool = False + self._flow_initialized: bool = False + + self._instance_id = str(uuid4())[:8] + + self.before_llm_call_hooks: list[Callable] = [] + self.after_llm_call_hooks: list[Callable] = [] + self.before_llm_call_hooks.extend(get_before_llm_call_hooks()) + self.after_llm_call_hooks.extend(get_after_llm_call_hooks()) + + if self.llm: + existing_stop = getattr(self.llm, "stop", []) + self.llm.stop = list( + set( + existing_stop + self.stop + if isinstance(existing_stop, list) + else self.stop + ) + ) + + self._state = AgentReActState() + + def _ensure_flow_initialized(self) -> None: + """Ensure Flow.__init__() has been called. + + This is deferred from __init__ to prevent FlowCreatedEvent emission + during agent setup when multiple executor instances are created. + Only the instance that actually executes via invoke() will emit events. + """ + if not self._flow_initialized: + # Now call Flow's __init__ which will replace self._state + # with Flow's managed state. Suppress flow events since this is + # an agent executor, not a user-facing flow. + super().__init__( + suppress_flow_events=True, + ) + self._flow_initialized = True + + @property + def use_stop_words(self) -> bool: + """Check to determine if stop words are being used. + + Returns: + bool: True if stop words should be used. + """ + return self.llm.supports_stop_words() if self.llm else False + + @property + def state(self) -> AgentReActState: + """Get state - returns temporary state if Flow not yet initialized. + + Flow initialization is deferred to prevent event emission during agent setup. + Returns the temporary state until invoke() is called. + """ + return self._state + + @property + def messages(self) -> list[LLMMessage]: + """Compatibility property for mixin - returns state messages.""" + return self._state.messages + + @property + def iterations(self) -> int: + """Compatibility property for mixin - returns state iterations.""" + return self._state.iterations + + @start() + def initialize_reasoning(self) -> Literal["initialized"]: + """Initialize the reasoning flow and emit agent start logs.""" + self._show_start_logs() + return "initialized" + + @listen("force_final_answer") + def force_final_answer(self) -> Literal["agent_finished"]: + """Force agent to provide final answer when max iterations exceeded.""" + formatted_answer = handle_max_iterations_exceeded( + formatted_answer=None, + printer=self._printer, + i18n=self._i18n, + messages=list(self.state.messages), + llm=self.llm, + callbacks=self.callbacks, + ) + + self.state.current_answer = formatted_answer + self.state.is_finished = True + + return "agent_finished" + + @listen("continue_reasoning") + def call_llm_and_parse(self) -> Literal["parsed", "parser_error", "context_error"]: + """Execute LLM call with hooks and parse the response. + + Returns routing decision based on parsing result. + """ + try: + enforce_rpm_limit(self.request_within_rpm_limit) + + answer = get_llm_response( + llm=self.llm, + messages=list(self.state.messages), + callbacks=self.callbacks, + printer=self._printer, + from_task=self.task, + from_agent=self.agent, + response_model=self.response_model, + executor_context=self, + ) + + # Parse the LLM response + formatted_answer = process_llm_response(answer, self.use_stop_words) + self.state.current_answer = formatted_answer + + if "Final Answer:" in answer and isinstance(formatted_answer, AgentAction): + warning_text = Text() + warning_text.append("⚠️ ", style="yellow bold") + warning_text.append( + f"LLM returned 'Final Answer:' but parsed as AgentAction (tool: {formatted_answer.tool})", + style="yellow", + ) + self._console.print(warning_text) + preview_text = Text() + preview_text.append("Answer preview: ", style="yellow") + preview_text.append(f"{answer[:200]}...", style="yellow dim") + self._console.print(preview_text) + + return "parsed" + + except OutputParserError as e: + # Store error context for recovery + self._last_parser_error = e or OutputParserError( + error="Unknown parser error" + ) + return "parser_error" + + except Exception as e: + if is_context_length_exceeded(e): + self._last_context_error = e + return "context_error" + if e.__class__.__module__.startswith("litellm"): + raise e + handle_unknown_error(self._printer, e) + raise + + @router(call_llm_and_parse) + def route_by_answer_type(self) -> Literal["execute_tool", "agent_finished"]: + """Route based on whether answer is AgentAction or AgentFinish.""" + if isinstance(self.state.current_answer, AgentAction): + return "execute_tool" + return "agent_finished" + + @listen("execute_tool") + def execute_tool_action(self) -> Literal["tool_completed", "tool_result_is_final"]: + """Execute the tool action and handle the result.""" + try: + action = cast(AgentAction, self.state.current_answer) + + # Extract fingerprint context for tool execution + fingerprint_context = {} + if ( + self.agent + and hasattr(self.agent, "security_config") + and hasattr(self.agent.security_config, "fingerprint") + ): + fingerprint_context = { + "agent_fingerprint": str(self.agent.security_config.fingerprint) + } + + # Execute the tool + tool_result = execute_tool_and_check_finality( + agent_action=action, + fingerprint_context=fingerprint_context, + tools=self.tools, + i18n=self._i18n, + agent_key=self.agent.key if self.agent else None, + agent_role=self.agent.role if self.agent else None, + tools_handler=self.tools_handler, + task=self.task, + agent=self.agent, + function_calling_llm=self.function_calling_llm, + crew=self.crew, + ) + + # Handle agent action and append observation to messages + result = self._handle_agent_action(action, tool_result) + self.state.current_answer = result + + # Invoke step callback if configured + self._invoke_step_callback(result) + + # Append result message to conversation state + if hasattr(result, "text"): + self._append_message_to_state(result.text) + + # Check if tool result became a final answer (result_as_answer flag) + if isinstance(result, AgentFinish): + self.state.is_finished = True + return "tool_result_is_final" + + return "tool_completed" + + except Exception as e: + error_text = Text() + error_text.append("❌ Error in tool execution: ", style="red bold") + error_text.append(str(e), style="red") + self._console.print(error_text) + raise + + @listen("initialized") + def continue_iteration(self) -> Literal["check_iteration"]: + """Bridge listener that connects iteration loop back to iteration check.""" + return "check_iteration" + + @router(or_(initialize_reasoning, continue_iteration)) + def check_max_iterations( + self, + ) -> Literal["force_final_answer", "continue_reasoning"]: + """Check if max iterations reached before proceeding with reasoning.""" + if has_reached_max_iterations(self.state.iterations, self.max_iter): + return "force_final_answer" + return "continue_reasoning" + + @router(execute_tool_action) + def increment_and_continue(self) -> Literal["initialized"]: + """Increment iteration counter and loop back for next iteration.""" + self.state.iterations += 1 + return "initialized" + + @listen(or_("agent_finished", "tool_result_is_final")) + def finalize(self) -> Literal["completed", "skipped"]: + """Finalize execution and emit completion logs.""" + if self.state.current_answer is None: + skip_text = Text() + skip_text.append("⚠️ ", style="yellow bold") + skip_text.append( + "Finalize called but no answer in state - skipping", style="yellow" + ) + self._console.print(skip_text) + return "skipped" + + if not isinstance(self.state.current_answer, AgentFinish): + skip_text = Text() + skip_text.append("⚠️ ", style="yellow bold") + skip_text.append( + f"Finalize called with {type(self.state.current_answer).__name__} instead of AgentFinish - skipping", + style="yellow", + ) + self._console.print(skip_text) + return "skipped" + + self.state.is_finished = True + + self._show_logs(self.state.current_answer) + + return "completed" + + @listen("parser_error") + def recover_from_parser_error(self) -> Literal["initialized"]: + """Recover from output parser errors and retry.""" + formatted_answer = handle_output_parser_exception( + e=self._last_parser_error, + messages=list(self.state.messages), + iterations=self.state.iterations, + log_error_after=self.log_error_after, + printer=self._printer, + ) + + if formatted_answer: + self.state.current_answer = formatted_answer + + self.state.iterations += 1 + + return "initialized" + + @listen("context_error") + def recover_from_context_length(self) -> Literal["initialized"]: + """Recover from context length errors and retry.""" + handle_context_length( + respect_context_window=self.respect_context_window, + printer=self._printer, + messages=self.state.messages, + llm=self.llm, + callbacks=self.callbacks, + i18n=self._i18n, + ) + + self.state.iterations += 1 + + return "initialized" + + def invoke(self, inputs: dict[str, Any]) -> dict[str, Any]: + """Execute agent with given inputs. + + Args: + inputs: Input dictionary containing prompt variables. + + Returns: + Dictionary with agent output. + """ + self._ensure_flow_initialized() + + with self._execution_lock: + if self._is_executing: + raise RuntimeError( + "Executor is already running. " + "Cannot invoke the same executor instance concurrently." + ) + self._is_executing = True + self._has_been_invoked = True + + try: + # Reset state for fresh execution + self.state.messages.clear() + self.state.iterations = 0 + self.state.current_answer = None + self.state.is_finished = False + + if "system" in self.prompt: + prompt = cast("SystemPromptResult", self.prompt) + system_prompt = self._format_prompt(prompt["system"], inputs) + user_prompt = self._format_prompt(prompt["user"], inputs) + self.state.messages.append( + format_message_for_llm(system_prompt, role="system") + ) + self.state.messages.append(format_message_for_llm(user_prompt)) + else: + user_prompt = self._format_prompt(self.prompt["prompt"], inputs) + self.state.messages.append(format_message_for_llm(user_prompt)) + + self.state.ask_for_human_input = bool( + inputs.get("ask_for_human_input", False) + ) + + self.kickoff() + + formatted_answer = self.state.current_answer + + if not isinstance(formatted_answer, AgentFinish): + raise RuntimeError( + "Agent execution ended without reaching a final answer." + ) + + if self.state.ask_for_human_input: + formatted_answer = self._handle_human_feedback(formatted_answer) + + self._create_short_term_memory(formatted_answer) + self._create_long_term_memory(formatted_answer) + self._create_external_memory(formatted_answer) + + return {"output": formatted_answer.output} + + except AssertionError: + fail_text = Text() + fail_text.append("❌ ", style="red bold") + fail_text.append( + "Agent failed to reach a final answer. This is likely a bug - please report it.", + style="red", + ) + self._console.print(fail_text) + raise + except Exception as e: + handle_unknown_error(self._printer, e) + raise + finally: + self._is_executing = False + + def _handle_agent_action( + self, formatted_answer: AgentAction, tool_result: ToolResult + ) -> AgentAction | AgentFinish: + """Process agent action and tool execution result. + + Args: + formatted_answer: Agent's action to execute. + tool_result: Result from tool execution. + + Returns: + Updated action or final answer. + """ + add_image_tool = self._i18n.tools("add_image") + if ( + isinstance(add_image_tool, dict) + and formatted_answer.tool.casefold().strip() + == add_image_tool.get("name", "").casefold().strip() + ): + self.state.messages.append( + {"role": "assistant", "content": tool_result.result} + ) + return formatted_answer + + return handle_agent_action_core( + formatted_answer=formatted_answer, + tool_result=tool_result, + messages=self.state.messages, + step_callback=self.step_callback, + show_logs=self._show_logs, + ) + + def _invoke_step_callback( + self, formatted_answer: AgentAction | AgentFinish + ) -> None: + """Invoke step callback if configured. + + Args: + formatted_answer: Current agent response. + """ + if self.step_callback: + self.step_callback(formatted_answer) + + def _append_message_to_state( + self, text: str, role: Literal["user", "assistant", "system"] = "assistant" + ) -> None: + """Add message to state conversation history. + + Args: + text: Message content. + role: Message role (default: assistant). + """ + self.state.messages.append(format_message_for_llm(text, role=role)) + + def _show_start_logs(self) -> None: + """Emit agent start event.""" + if self.agent is None: + raise ValueError("Agent cannot be None") + + crewai_event_bus.emit( + self.agent, + AgentLogsStartedEvent( + agent_role=self.agent.role, + task_description=(self.task.description if self.task else "Not Found"), + verbose=self.agent.verbose + or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)), + ), + ) + + def _show_logs(self, formatted_answer: AgentAction | AgentFinish) -> None: + """Emit agent execution event. + + Args: + formatted_answer: Agent's response to log. + """ + if self.agent is None: + raise ValueError("Agent cannot be None") + + crewai_event_bus.emit( + self.agent, + AgentLogsExecutionEvent( + agent_role=self.agent.role, + formatted_answer=formatted_answer, + verbose=self.agent.verbose + or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)), + ), + ) + + def _handle_crew_training_output( + self, result: AgentFinish, human_feedback: str | None = None + ) -> None: + """Save training data for crew training mode. + + Args: + result: Agent's final output. + human_feedback: Optional feedback from human. + """ + agent_id = str(self.agent.id) + train_iteration = ( + getattr(self.crew, "_train_iteration", None) if self.crew else None + ) + + if train_iteration is None or not isinstance(train_iteration, int): + train_error = Text() + train_error.append("❌ ", style="red bold") + train_error.append( + "Invalid or missing train iteration. Cannot save training data.", + style="red", + ) + self._console.print(train_error) + return + + training_handler = CrewTrainingHandler(TRAINING_DATA_FILE) + training_data = training_handler.load() or {} + + # Initialize or retrieve agent's training data + agent_training_data = training_data.get(agent_id, {}) + + if human_feedback is not None: + # Save initial output and human feedback + agent_training_data[train_iteration] = { + "initial_output": result.output, + "human_feedback": human_feedback, + } + else: + # Save improved output + if train_iteration in agent_training_data: + agent_training_data[train_iteration]["improved_output"] = result.output + else: + train_error = Text() + train_error.append("❌ ", style="red bold") + train_error.append( + f"No existing training data for agent {agent_id} and iteration " + f"{train_iteration}. Cannot save improved output.", + style="red", + ) + self._console.print(train_error) + return + + # Update the training data and save + training_data[agent_id] = agent_training_data + training_handler.save(training_data) + + @staticmethod + def _format_prompt(prompt: str, inputs: dict[str, str]) -> str: + """Format prompt template with input values. + + Args: + prompt: Template string. + inputs: Values to substitute. + + Returns: + Formatted prompt. + """ + prompt = prompt.replace("{input}", inputs["input"]) + prompt = prompt.replace("{tool_names}", inputs["tool_names"]) + return prompt.replace("{tools}", inputs["tools"]) + + def _handle_human_feedback(self, formatted_answer: AgentFinish) -> AgentFinish: + """Process human feedback and refine answer. + + Args: + formatted_answer: Initial agent result. + + Returns: + Final answer after feedback. + """ + human_feedback = self._ask_human_input(formatted_answer.output) + + if self._is_training_mode(): + return self._handle_training_feedback(formatted_answer, human_feedback) + + return self._handle_regular_feedback(formatted_answer, human_feedback) + + def _is_training_mode(self) -> bool: + """Check if training mode is active. + + Returns: + True if in training mode. + """ + return bool(self.crew and self.crew._train) + + def _handle_training_feedback( + self, initial_answer: AgentFinish, feedback: str + ) -> AgentFinish: + """Process training feedback and generate improved answer. + + Args: + initial_answer: Initial agent output. + feedback: Training feedback. + + Returns: + Improved answer. + """ + self._handle_crew_training_output(initial_answer, feedback) + self.state.messages.append( + format_message_for_llm( + self._i18n.slice("feedback_instructions").format(feedback=feedback) + ) + ) + + # Re-run flow for improved answer + self.state.iterations = 0 + self.state.is_finished = False + self.state.current_answer = None + + self.kickoff() + + # Get improved answer from state + improved_answer = self.state.current_answer + if not isinstance(improved_answer, AgentFinish): + raise RuntimeError( + "Training feedback iteration did not produce final answer" + ) + + self._handle_crew_training_output(improved_answer) + self.state.ask_for_human_input = False + return improved_answer + + def _handle_regular_feedback( + self, current_answer: AgentFinish, initial_feedback: str + ) -> AgentFinish: + """Process regular feedback iteratively until user is satisfied. + + Args: + current_answer: Current agent output. + initial_feedback: Initial user feedback. + + Returns: + Final answer after iterations. + """ + feedback = initial_feedback + answer = current_answer + + while self.state.ask_for_human_input: + if feedback.strip() == "": + self.state.ask_for_human_input = False + else: + answer = self._process_feedback_iteration(feedback) + feedback = self._ask_human_input(answer.output) + + return answer + + def _process_feedback_iteration(self, feedback: str) -> AgentFinish: + """Process a single feedback iteration and generate updated response. + + Args: + feedback: User feedback. + + Returns: + Updated agent response. + """ + self.state.messages.append( + format_message_for_llm( + self._i18n.slice("feedback_instructions").format(feedback=feedback) + ) + ) + + # Re-run flow + self.state.iterations = 0 + self.state.is_finished = False + self.state.current_answer = None + + self.kickoff() + + # Get answer from state + answer = self.state.current_answer + if not isinstance(answer, AgentFinish): + raise RuntimeError("Feedback iteration did not produce final answer") + + return answer + + @classmethod + def __get_pydantic_core_schema__( + cls, _source_type: Any, _handler: GetCoreSchemaHandler + ) -> CoreSchema: + """Generate Pydantic core schema for Protocol compatibility. + + Allows the executor to be used in Pydantic models without + requiring arbitrary_types_allowed=True. + """ + return core_schema.any_schema() diff --git a/lib/crewai/src/crewai/experimental/evaluation/agent_evaluator.py b/lib/crewai/src/crewai/experimental/evaluation/agent_evaluator.py index 7114e8c40..3b9610839 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/agent_evaluator.py +++ b/lib/crewai/src/crewai/experimental/evaluation/agent_evaluator.py @@ -1,8 +1,9 @@ +from __future__ import annotations + from collections.abc import Sequence import threading -from typing import Any +from typing import TYPE_CHECKING, Any -from crewai.agent.core import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.events.event_bus import crewai_event_bus from crewai.events.types.agent_events import ( @@ -28,6 +29,10 @@ from crewai.experimental.evaluation.evaluation_listener import ( from crewai.task import Task +if TYPE_CHECKING: + from crewai.agent import Agent + + class ExecutionState: current_agent_id: str | None = None current_task_id: str | None = None diff --git a/lib/crewai/src/crewai/experimental/evaluation/base_evaluator.py b/lib/crewai/src/crewai/experimental/evaluation/base_evaluator.py index 69d1bb5c3..bcc8f9801 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/base_evaluator.py +++ b/lib/crewai/src/crewai/experimental/evaluation/base_evaluator.py @@ -1,17 +1,22 @@ +from __future__ import annotations + import abc import enum from enum import Enum -from typing import Any +from typing import TYPE_CHECKING, Any from pydantic import BaseModel, Field -from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.llm import BaseLLM from crewai.task import Task from crewai.utilities.llm_utils import create_llm +if TYPE_CHECKING: + from crewai.agent import Agent + + class MetricCategory(enum.Enum): GOAL_ALIGNMENT = "goal_alignment" SEMANTIC_QUALITY = "semantic_quality" diff --git a/lib/crewai/src/crewai/experimental/evaluation/experiment/runner.py b/lib/crewai/src/crewai/experimental/evaluation/experiment/runner.py index a01457e85..22a254053 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/experiment/runner.py +++ b/lib/crewai/src/crewai/experimental/evaluation/experiment/runner.py @@ -1,8 +1,9 @@ +from __future__ import annotations + from collections import defaultdict from hashlib import md5 -from typing import Any +from typing import TYPE_CHECKING, Any -from crewai import Agent, Crew from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.experimental.evaluation import AgentEvaluator, create_default_evaluator from crewai.experimental.evaluation.evaluation_display import ( @@ -17,6 +18,11 @@ from crewai.experimental.evaluation.experiment.result_display import ( ) +if TYPE_CHECKING: + from crewai.agent import Agent + from crewai.crew import Crew + + class ExperimentRunner: def __init__(self, dataset: list[dict[str, Any]]): self.dataset = dataset or [] diff --git a/lib/crewai/src/crewai/experimental/evaluation/metrics/goal_metrics.py b/lib/crewai/src/crewai/experimental/evaluation/metrics/goal_metrics.py index 9758a7fe4..0075c3c49 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/metrics/goal_metrics.py +++ b/lib/crewai/src/crewai/experimental/evaluation/metrics/goal_metrics.py @@ -1,6 +1,7 @@ -from typing import Any +from __future__ import annotations + +from typing import TYPE_CHECKING, Any -from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.experimental.evaluation.base_evaluator import ( BaseEvaluator, @@ -12,6 +13,10 @@ from crewai.task import Task from crewai.utilities.types import LLMMessage +if TYPE_CHECKING: + from crewai.agent import Agent + + class GoalAlignmentEvaluator(BaseEvaluator): @property def metric_category(self) -> MetricCategory: diff --git a/lib/crewai/src/crewai/experimental/evaluation/metrics/reasoning_metrics.py b/lib/crewai/src/crewai/experimental/evaluation/metrics/reasoning_metrics.py index 4531103f5..67c272879 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/metrics/reasoning_metrics.py +++ b/lib/crewai/src/crewai/experimental/evaluation/metrics/reasoning_metrics.py @@ -6,15 +6,16 @@ This module provides evaluator implementations for: - Thinking-to-action ratio """ +from __future__ import annotations + from collections.abc import Sequence from enum import Enum import logging import re -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.experimental.evaluation.base_evaluator import ( BaseEvaluator, @@ -27,6 +28,10 @@ from crewai.tasks.task_output import TaskOutput from crewai.utilities.types import LLMMessage +if TYPE_CHECKING: + from crewai.agent import Agent + + class ReasoningPatternType(Enum): EFFICIENT = "efficient" # Good reasoning flow LOOP = "loop" # Agent is stuck in a loop diff --git a/lib/crewai/src/crewai/experimental/evaluation/metrics/semantic_quality_metrics.py b/lib/crewai/src/crewai/experimental/evaluation/metrics/semantic_quality_metrics.py index 7a0bd488a..692c4f84a 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/metrics/semantic_quality_metrics.py +++ b/lib/crewai/src/crewai/experimental/evaluation/metrics/semantic_quality_metrics.py @@ -1,6 +1,7 @@ -from typing import Any +from __future__ import annotations + +from typing import TYPE_CHECKING, Any -from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.experimental.evaluation.base_evaluator import ( BaseEvaluator, @@ -12,6 +13,10 @@ from crewai.task import Task from crewai.utilities.types import LLMMessage +if TYPE_CHECKING: + from crewai.agent import Agent + + class SemanticQualityEvaluator(BaseEvaluator): @property def metric_category(self) -> MetricCategory: diff --git a/lib/crewai/src/crewai/experimental/evaluation/metrics/tools_metrics.py b/lib/crewai/src/crewai/experimental/evaluation/metrics/tools_metrics.py index baf7c69c5..99580dc3f 100644 --- a/lib/crewai/src/crewai/experimental/evaluation/metrics/tools_metrics.py +++ b/lib/crewai/src/crewai/experimental/evaluation/metrics/tools_metrics.py @@ -1,7 +1,8 @@ -import json -from typing import Any +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any -from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.experimental.evaluation.base_evaluator import ( BaseEvaluator, @@ -13,6 +14,10 @@ from crewai.task import Task from crewai.utilities.types import LLMMessage +if TYPE_CHECKING: + from crewai.agent import Agent + + class ToolSelectionEvaluator(BaseEvaluator): @property def metric_category(self) -> MetricCategory: diff --git a/lib/crewai/src/crewai/flow/flow.py b/lib/crewai/src/crewai/flow/flow.py index 26b8c7190..ade5b6f1b 100644 --- a/lib/crewai/src/crewai/flow/flow.py +++ b/lib/crewai/src/crewai/flow/flow.py @@ -459,7 +459,10 @@ class FlowMeta(type): ): routers.add(attr_name) # Get router paths from the decorator attribute - if hasattr(attr_value, "__router_paths__") and attr_value.__router_paths__: + if ( + hasattr(attr_value, "__router_paths__") + and attr_value.__router_paths__ + ): router_paths[attr_name] = attr_value.__router_paths__ else: possible_returns = get_possible_return_constants(attr_value) @@ -501,6 +504,7 @@ class Flow(Generic[T], metaclass=FlowMeta): self, persistence: FlowPersistence | None = None, tracing: bool | None = None, + suppress_flow_events: bool = False, **kwargs: Any, ) -> None: """Initialize a new Flow instance. @@ -508,6 +512,7 @@ class Flow(Generic[T], metaclass=FlowMeta): Args: persistence: Optional persistence backend for storing flow states tracing: Whether to enable tracing. True=always enable, False=always disable, None=check environment/user settings + suppress_flow_events: Whether to suppress flow event emissions (internal use) **kwargs: Additional state values to initialize or override """ # Initialize basic instance attributes @@ -526,6 +531,7 @@ class Flow(Generic[T], metaclass=FlowMeta): self.human_feedback_history: list[HumanFeedbackResult] = [] self.last_human_feedback: HumanFeedbackResult | None = None self._pending_feedback_context: PendingFeedbackContext | None = None + self.suppress_flow_events: bool = suppress_flow_events # Initialize state with initial values self._state = self._create_initial_state() @@ -539,13 +545,14 @@ class Flow(Generic[T], metaclass=FlowMeta): if kwargs: self._initialize_state(kwargs) - crewai_event_bus.emit( - self, - FlowCreatedEvent( - type="flow_created", - flow_name=self.name or self.__class__.__name__, - ), - ) + if not self.suppress_flow_events: + crewai_event_bus.emit( + self, + FlowCreatedEvent( + type="flow_created", + flow_name=self.name or self.__class__.__name__, + ), + ) # Register all flow-related methods for method_name in dir(self): @@ -672,6 +679,7 @@ class Flow(Generic[T], metaclass=FlowMeta): result = flow.resume(feedback) return result + # In an async handler, use resume_async instead: async def handle_feedback_async(flow_id: str, feedback: str): flow = MyFlow.from_pending(flow_id) @@ -1307,19 +1315,20 @@ class Flow(Generic[T], metaclass=FlowMeta): self._initialize_state(filtered_inputs) # Emit FlowStartedEvent and log the start of the flow. - future = crewai_event_bus.emit( - self, - FlowStartedEvent( - type="flow_started", - flow_name=self.name or self.__class__.__name__, - inputs=inputs, - ), - ) - if future: - self._event_futures.append(future) - self._log_flow_event( - f"Flow started with ID: {self.flow_id}", color="bold magenta" - ) + if not self.suppress_flow_events: + future = crewai_event_bus.emit( + self, + FlowStartedEvent( + type="flow_started", + flow_name=self.name or self.__class__.__name__, + inputs=inputs, + ), + ) + if future: + self._event_futures.append(future) + self._log_flow_event( + f"Flow started with ID: {self.flow_id}", color="bold magenta" + ) if inputs is not None and "id" not in inputs: self._initialize_state(inputs) @@ -1391,17 +1400,18 @@ class Flow(Generic[T], metaclass=FlowMeta): final_output = self._method_outputs[-1] if self._method_outputs else None - future = crewai_event_bus.emit( - self, - FlowFinishedEvent( - type="flow_finished", - flow_name=self.name or self.__class__.__name__, - result=final_output, - state=self._copy_and_serialize_state(), - ), - ) - if future: - self._event_futures.append(future) + if not self.suppress_flow_events: + future = crewai_event_bus.emit( + self, + FlowFinishedEvent( + type="flow_finished", + flow_name=self.name or self.__class__.__name__, + result=final_output, + state=self._copy_and_serialize_state(), + ), + ) + if future: + self._event_futures.append(future) if self._event_futures: await asyncio.gather( @@ -1537,18 +1547,19 @@ class Flow(Generic[T], metaclass=FlowMeta): kwargs or {} ) - future = crewai_event_bus.emit( - self, - MethodExecutionStartedEvent( - type="method_execution_started", - method_name=method_name, - flow_name=self.name or self.__class__.__name__, - params=dumped_params, - state=self._copy_and_serialize_state(), - ), - ) - if future: - self._event_futures.append(future) + if not self.suppress_flow_events: + future = crewai_event_bus.emit( + self, + MethodExecutionStartedEvent( + type="method_execution_started", + method_name=method_name, + flow_name=self.name or self.__class__.__name__, + params=dumped_params, + state=self._copy_and_serialize_state(), + ), + ) + if future: + self._event_futures.append(future) result = ( await method(*args, **kwargs) @@ -1563,41 +1574,32 @@ class Flow(Generic[T], metaclass=FlowMeta): self._completed_methods.add(method_name) - future = crewai_event_bus.emit( - self, - MethodExecutionFinishedEvent( - type="method_execution_finished", - method_name=method_name, - flow_name=self.name or self.__class__.__name__, - state=self._copy_and_serialize_state(), - result=result, - ), - ) - if future: - self._event_futures.append(future) - - return result - except Exception as e: - # Check if this is a HumanFeedbackPending exception (paused, not failed) - from crewai.flow.async_feedback.types import HumanFeedbackPending - - if isinstance(e, HumanFeedbackPending): - # Emit paused event instead of failed + if not self.suppress_flow_events: future = crewai_event_bus.emit( self, - MethodExecutionPausedEvent( - type="method_execution_paused", + MethodExecutionFinishedEvent( + type="method_execution_finished", method_name=method_name, flow_name=self.name or self.__class__.__name__, state=self._copy_and_serialize_state(), - flow_id=e.context.flow_id, - message=e.context.message, - emit=e.context.emit, + result=result, ), ) if future: self._event_futures.append(future) - raise e + + return result + except Exception as e: + if not self.suppress_flow_events: + # Check if this is a HumanFeedbackPending exception (paused, not failed) + from crewai.flow.async_feedback.types import HumanFeedbackPending + + if isinstance(e, HumanFeedbackPending): + # Auto-save pending feedback (create default persistence if needed) + if self._persistence is None: + from crewai.flow.persistence import SQLiteFlowPersistence + + self._persistence = SQLiteFlowPersistence() # Regular failure future = crewai_event_bus.emit( @@ -1644,7 +1646,9 @@ class Flow(Generic[T], metaclass=FlowMeta): """ # First, handle routers repeatedly until no router triggers anymore router_results = [] - router_result_to_feedback: dict[str, Any] = {} # Map outcome -> HumanFeedbackResult + router_result_to_feedback: dict[ + str, Any + ] = {} # Map outcome -> HumanFeedbackResult current_trigger = trigger_method current_result = result # Track the result to pass to each router @@ -1963,7 +1967,9 @@ class Flow(Generic[T], metaclass=FlowMeta): # Show message and prompt for feedback formatter.console.print(message, style="yellow") - formatter.console.print("(Press Enter to skip, or type your feedback)\n", style="cyan") + formatter.console.print( + "(Press Enter to skip, or type your feedback)\n", style="cyan" + ) feedback = input("Your feedback: ").strip() diff --git a/lib/crewai/tests/agents/test_agent.py b/lib/crewai/tests/agents/test_agent.py index 26aec7252..d2ec33617 100644 --- a/lib/crewai/tests/agents/test_agent.py +++ b/lib/crewai/tests/agents/test_agent.py @@ -1178,6 +1178,7 @@ def test_system_and_prompt_template(): {{ .Response }}<|eot_id|>""", ) + agent.create_agent_executor() expected_prompt = """<|start_header_id|>system<|end_header_id|> @@ -1442,6 +1443,8 @@ def test_agent_max_retry_limit(): human_input=True, ) + agent.create_agent_executor(task=task) + error_message = "Error happening while sending prompt to model." with patch.object( CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke @@ -1503,9 +1506,8 @@ def test_agent_with_custom_stop_words(): ) assert isinstance(agent.llm, BaseLLM) - assert set(agent.llm.stop) == set([*stop_words, "\nObservation:"]) + assert set(agent.llm.stop) == set(stop_words) assert all(word in agent.llm.stop for word in stop_words) - assert "\nObservation:" in agent.llm.stop def test_agent_with_callbacks(): @@ -1629,6 +1631,8 @@ def test_handle_context_length_exceeds_limit_cli_no(): ) task = Task(description="test task", agent=agent, expected_output="test output") + agent.create_agent_executor(task=task) + with patch.object( CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke ) as private_mock: @@ -1679,8 +1683,8 @@ def test_agent_with_all_llm_attributes(): assert agent.llm.temperature == 0.7 assert agent.llm.top_p == 0.9 # assert agent.llm.n == 1 - assert set(agent.llm.stop) == set(["STOP", "END", "\nObservation:"]) - assert all(word in agent.llm.stop for word in ["STOP", "END", "\nObservation:"]) + assert set(agent.llm.stop) == set(["STOP", "END"]) + assert all(word in agent.llm.stop for word in ["STOP", "END"]) assert agent.llm.max_tokens == 100 assert agent.llm.presence_penalty == 0.1 assert agent.llm.frequency_penalty == 0.1 diff --git a/lib/crewai/tests/agents/test_crew_agent_executor_flow.py b/lib/crewai/tests/agents/test_crew_agent_executor_flow.py new file mode 100644 index 000000000..36fd887eb --- /dev/null +++ b/lib/crewai/tests/agents/test_crew_agent_executor_flow.py @@ -0,0 +1,479 @@ +"""Unit tests for CrewAgentExecutorFlow. + +Tests the Flow-based agent executor implementation including state management, +flow methods, routing logic, and error handling. +""" + +from unittest.mock import Mock, patch + +import pytest + +from crewai.experimental.crew_agent_executor_flow import ( + AgentReActState, + CrewAgentExecutorFlow, +) +from crewai.agents.parser import AgentAction, AgentFinish + +class TestAgentReActState: + """Test AgentReActState Pydantic model.""" + + def test_state_initialization(self): + """Test AgentReActState initialization with defaults.""" + state = AgentReActState() + assert state.iterations == 0 + assert state.messages == [] + assert state.current_answer is None + assert state.is_finished is False + assert state.ask_for_human_input is False + + def test_state_with_values(self): + """Test AgentReActState initialization with values.""" + messages = [{"role": "user", "content": "test"}] + state = AgentReActState( + messages=messages, + iterations=5, + current_answer=AgentFinish(thought="thinking", output="done", text="final"), + is_finished=True, + ask_for_human_input=True, + ) + assert state.messages == messages + assert state.iterations == 5 + assert isinstance(state.current_answer, AgentFinish) + assert state.is_finished is True + assert state.ask_for_human_input is True + + +class TestCrewAgentExecutorFlow: + """Test CrewAgentExecutorFlow class.""" + + @pytest.fixture + def mock_dependencies(self): + """Create mock dependencies for executor.""" + llm = Mock() + llm.supports_stop_words.return_value = True + + task = Mock() + task.description = "Test task" + task.human_input = False + task.response_model = None + + crew = Mock() + crew.verbose = False + crew._train = False + + agent = Mock() + agent.id = "test-agent-id" + agent.role = "Test Agent" + agent.verbose = False + agent.key = "test-key" + + prompt = {"prompt": "Test prompt with {input}, {tool_names}, {tools}"} + + tools = [] + tools_handler = Mock() + + return { + "llm": llm, + "task": task, + "crew": crew, + "agent": agent, + "prompt": prompt, + "max_iter": 10, + "tools": tools, + "tools_names": "", + "stop_words": ["Observation"], + "tools_description": "", + "tools_handler": tools_handler, + } + + def test_executor_initialization(self, mock_dependencies): + """Test CrewAgentExecutorFlow initialization.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + + assert executor.llm == mock_dependencies["llm"] + assert executor.task == mock_dependencies["task"] + assert executor.agent == mock_dependencies["agent"] + assert executor.crew == mock_dependencies["crew"] + assert executor.max_iter == 10 + assert executor.use_stop_words is True + + def test_initialize_reasoning(self, mock_dependencies): + """Test flow entry point.""" + with patch.object( + CrewAgentExecutorFlow, "_show_start_logs" + ) as mock_show_start: + executor = CrewAgentExecutorFlow(**mock_dependencies) + result = executor.initialize_reasoning() + + assert result == "initialized" + mock_show_start.assert_called_once() + + def test_check_max_iterations_not_reached(self, mock_dependencies): + """Test routing when iterations < max.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.iterations = 5 + + result = executor.check_max_iterations() + assert result == "continue_reasoning" + + def test_check_max_iterations_reached(self, mock_dependencies): + """Test routing when iterations >= max.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.iterations = 10 + + result = executor.check_max_iterations() + assert result == "force_final_answer" + + def test_route_by_answer_type_action(self, mock_dependencies): + """Test routing for AgentAction.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.current_answer = AgentAction( + thought="thinking", tool="search", tool_input="query", text="action text" + ) + + result = executor.route_by_answer_type() + assert result == "execute_tool" + + def test_route_by_answer_type_finish(self, mock_dependencies): + """Test routing for AgentFinish.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.current_answer = AgentFinish( + thought="final thoughts", output="Final answer", text="complete" + ) + + result = executor.route_by_answer_type() + assert result == "agent_finished" + + def test_continue_iteration(self, mock_dependencies): + """Test iteration continuation.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + + result = executor.continue_iteration() + + assert result == "check_iteration" + + def test_finalize_success(self, mock_dependencies): + """Test finalize with valid AgentFinish.""" + with patch.object(CrewAgentExecutorFlow, "_show_logs") as mock_show_logs: + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.current_answer = AgentFinish( + thought="final thinking", output="Done", text="complete" + ) + + result = executor.finalize() + + assert result == "completed" + assert executor.state.is_finished is True + mock_show_logs.assert_called_once() + + def test_finalize_failure(self, mock_dependencies): + """Test finalize skips when given AgentAction instead of AgentFinish.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.current_answer = AgentAction( + thought="thinking", tool="search", tool_input="query", text="action text" + ) + + result = executor.finalize() + + # Should return "skipped" and not set is_finished + assert result == "skipped" + assert executor.state.is_finished is False + + def test_format_prompt(self, mock_dependencies): + """Test prompt formatting.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + inputs = {"input": "test input", "tool_names": "tool1, tool2", "tools": "desc"} + + result = executor._format_prompt("Prompt {input} {tool_names} {tools}", inputs) + + assert "test input" in result + assert "tool1, tool2" in result + assert "desc" in result + + def test_is_training_mode_false(self, mock_dependencies): + """Test training mode detection when not in training.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + assert executor._is_training_mode() is False + + def test_is_training_mode_true(self, mock_dependencies): + """Test training mode detection when in training.""" + mock_dependencies["crew"]._train = True + executor = CrewAgentExecutorFlow(**mock_dependencies) + assert executor._is_training_mode() is True + + def test_append_message_to_state(self, mock_dependencies): + """Test message appending to state.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + initial_count = len(executor.state.messages) + + executor._append_message_to_state("test message") + + assert len(executor.state.messages) == initial_count + 1 + assert executor.state.messages[-1]["content"] == "test message" + + def test_invoke_step_callback(self, mock_dependencies): + """Test step callback invocation.""" + callback = Mock() + mock_dependencies["step_callback"] = callback + + executor = CrewAgentExecutorFlow(**mock_dependencies) + answer = AgentFinish(thought="thinking", output="test", text="final") + + executor._invoke_step_callback(answer) + + callback.assert_called_once_with(answer) + + def test_invoke_step_callback_none(self, mock_dependencies): + """Test step callback when none provided.""" + mock_dependencies["step_callback"] = None + executor = CrewAgentExecutorFlow(**mock_dependencies) + + # Should not raise error + executor._invoke_step_callback( + AgentFinish(thought="thinking", output="test", text="final") + ) + + @patch("crewai.experimental.crew_agent_executor_flow.handle_output_parser_exception") + def test_recover_from_parser_error( + self, mock_handle_exception, mock_dependencies + ): + """Test recovery from OutputParserError.""" + from crewai.agents.parser import OutputParserError + + mock_handle_exception.return_value = None + + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor._last_parser_error = OutputParserError("test error") + initial_iterations = executor.state.iterations + + result = executor.recover_from_parser_error() + + assert result == "initialized" + assert executor.state.iterations == initial_iterations + 1 + mock_handle_exception.assert_called_once() + + @patch("crewai.experimental.crew_agent_executor_flow.handle_context_length") + def test_recover_from_context_length( + self, mock_handle_context, mock_dependencies + ): + """Test recovery from context length error.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor._last_context_error = Exception("context too long") + initial_iterations = executor.state.iterations + + result = executor.recover_from_context_length() + + assert result == "initialized" + assert executor.state.iterations == initial_iterations + 1 + mock_handle_context.assert_called_once() + + def test_use_stop_words_property(self, mock_dependencies): + """Test use_stop_words property.""" + mock_dependencies["llm"].supports_stop_words.return_value = True + executor = CrewAgentExecutorFlow(**mock_dependencies) + assert executor.use_stop_words is True + + mock_dependencies["llm"].supports_stop_words.return_value = False + executor = CrewAgentExecutorFlow(**mock_dependencies) + assert executor.use_stop_words is False + + def test_compatibility_properties(self, mock_dependencies): + """Test compatibility properties for mixin.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.messages = [{"role": "user", "content": "test"}] + executor.state.iterations = 5 + + # Test that compatibility properties return state values + assert executor.messages == executor.state.messages + assert executor.iterations == executor.state.iterations + + +class TestFlowErrorHandling: + """Test error handling in flow methods.""" + + @pytest.fixture + def mock_dependencies(self): + """Create mock dependencies.""" + llm = Mock() + llm.supports_stop_words.return_value = True + + task = Mock() + task.description = "Test task" + + crew = Mock() + agent = Mock() + agent.role = "Test Agent" + agent.verbose = False + + prompt = {"prompt": "Test {input}"} + + return { + "llm": llm, + "task": task, + "crew": crew, + "agent": agent, + "prompt": prompt, + "max_iter": 10, + "tools": [], + "tools_names": "", + "stop_words": [], + "tools_description": "", + "tools_handler": Mock(), + } + + @patch("crewai.experimental.crew_agent_executor_flow.get_llm_response") + @patch("crewai.experimental.crew_agent_executor_flow.enforce_rpm_limit") + def test_call_llm_parser_error( + self, mock_enforce_rpm, mock_get_llm, mock_dependencies + ): + """Test call_llm_and_parse handles OutputParserError.""" + from crewai.agents.parser import OutputParserError + + mock_enforce_rpm.return_value = None + mock_get_llm.side_effect = OutputParserError("parse failed") + + executor = CrewAgentExecutorFlow(**mock_dependencies) + result = executor.call_llm_and_parse() + + assert result == "parser_error" + assert executor._last_parser_error is not None + + @patch("crewai.experimental.crew_agent_executor_flow.get_llm_response") + @patch("crewai.experimental.crew_agent_executor_flow.enforce_rpm_limit") + @patch("crewai.experimental.crew_agent_executor_flow.is_context_length_exceeded") + def test_call_llm_context_error( + self, + mock_is_context_exceeded, + mock_enforce_rpm, + mock_get_llm, + mock_dependencies, + ): + """Test call_llm_and_parse handles context length error.""" + mock_enforce_rpm.return_value = None + mock_get_llm.side_effect = Exception("context length") + mock_is_context_exceeded.return_value = True + + executor = CrewAgentExecutorFlow(**mock_dependencies) + result = executor.call_llm_and_parse() + + assert result == "context_error" + assert executor._last_context_error is not None + + +class TestFlowInvoke: + """Test the invoke method that maintains backward compatibility.""" + + @pytest.fixture + def mock_dependencies(self): + """Create mock dependencies.""" + llm = Mock() + task = Mock() + task.description = "Test" + task.human_input = False + + crew = Mock() + crew._short_term_memory = None + crew._long_term_memory = None + crew._entity_memory = None + crew._external_memory = None + + agent = Mock() + agent.role = "Test" + agent.verbose = False + + prompt = {"prompt": "Test {input} {tool_names} {tools}"} + + return { + "llm": llm, + "task": task, + "crew": crew, + "agent": agent, + "prompt": prompt, + "max_iter": 10, + "tools": [], + "tools_names": "", + "stop_words": [], + "tools_description": "", + "tools_handler": Mock(), + } + + @patch.object(CrewAgentExecutorFlow, "kickoff") + @patch.object(CrewAgentExecutorFlow, "_create_short_term_memory") + @patch.object(CrewAgentExecutorFlow, "_create_long_term_memory") + @patch.object(CrewAgentExecutorFlow, "_create_external_memory") + def test_invoke_success( + self, + mock_external_memory, + mock_long_term_memory, + mock_short_term_memory, + mock_kickoff, + mock_dependencies, + ): + """Test successful invoke without human feedback.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + + # Mock kickoff to set the final answer in state + def mock_kickoff_side_effect(): + executor.state.current_answer = AgentFinish( + thought="final thinking", output="Final result", text="complete" + ) + + mock_kickoff.side_effect = mock_kickoff_side_effect + + inputs = {"input": "test", "tool_names": "", "tools": ""} + result = executor.invoke(inputs) + + assert result == {"output": "Final result"} + mock_kickoff.assert_called_once() + mock_short_term_memory.assert_called_once() + mock_long_term_memory.assert_called_once() + mock_external_memory.assert_called_once() + + @patch.object(CrewAgentExecutorFlow, "kickoff") + def test_invoke_failure_no_agent_finish(self, mock_kickoff, mock_dependencies): + """Test invoke fails without AgentFinish.""" + executor = CrewAgentExecutorFlow(**mock_dependencies) + executor.state.current_answer = AgentAction( + thought="thinking", tool="test", tool_input="test", text="action text" + ) + + inputs = {"input": "test", "tool_names": "", "tools": ""} + + with pytest.raises(RuntimeError, match="without reaching a final answer"): + executor.invoke(inputs) + + @patch.object(CrewAgentExecutorFlow, "kickoff") + @patch.object(CrewAgentExecutorFlow, "_create_short_term_memory") + @patch.object(CrewAgentExecutorFlow, "_create_long_term_memory") + @patch.object(CrewAgentExecutorFlow, "_create_external_memory") + def test_invoke_with_system_prompt( + self, + mock_external_memory, + mock_long_term_memory, + mock_short_term_memory, + mock_kickoff, + mock_dependencies, + ): + """Test invoke with system prompt configuration.""" + mock_dependencies["prompt"] = { + "system": "System: {input}", + "user": "User: {input} {tool_names} {tools}", + } + executor = CrewAgentExecutorFlow(**mock_dependencies) + + def mock_kickoff_side_effect(): + executor.state.current_answer = AgentFinish( + thought="final thoughts", output="Done", text="complete" + ) + + mock_kickoff.side_effect = mock_kickoff_side_effect + + inputs = {"input": "test", "tool_names": "", "tools": ""} + result = executor.invoke(inputs) + mock_short_term_memory.assert_called_once() + mock_long_term_memory.assert_called_once() + mock_external_memory.assert_called_once() + mock_kickoff.assert_called_once() + + assert result == {"output": "Done"} + assert len(executor.state.messages) >= 2 diff --git a/uv.lock b/uv.lock index 42fb449de..448593726 100644 --- a/uv.lock +++ b/uv.lock @@ -559,7 +559,7 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, { name = "uvicorn" }, { name = "websockets" }, ] @@ -609,7 +609,7 @@ dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/c1/8c4c199ae1663feee579a15861e34f10b29da11ae6ea0ad7b6a847ef3823/botocore-1.40.70.tar.gz", hash = "sha256:61b1f2cecd54d1b28a081116fa113b97bf4e17da57c62ae2c2751fe4c528af1f", size = 14444592, upload-time = "2025-11-10T20:29:04.046Z" } wheels = [ @@ -618,14 +618,14 @@ wheels = [ [[package]] name = "botocore-stubs" -version = "1.42.3" +version = "1.42.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-awscrt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/0e/d00b9b8d7e8f21e6089daeabfea401d68952e5ee9a76cd8040f035fd4d36/botocore_stubs-1.42.3.tar.gz", hash = "sha256:fa18ae8da1b548de7ebd9ce047141ce61901a9ef494e2bf85e568c056c9cd0c1", size = 42395, upload-time = "2025-12-04T18:41:01.518Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/b1/556e80eb0354f0a49c215c5d93ff3c5ec32967e1f404c75c638314524eac/botocore_stubs-1.42.6.tar.gz", hash = "sha256:ba3e770966c76b9eb9a6b300fd7d9b1c4021bac91dde6652b555c4a0e0b73392", size = 42416, upload-time = "2025-12-09T23:26:08.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/fb/e3cc821f7efafdf9fa36ac95e1502a0271612b1a8a943b27a427ed3a316f/botocore_stubs-1.42.3-py3-none-any.whl", hash = "sha256:66abcf697136fe8c1337b97f83a8d72b28ed7971459974fa3d99ae2057a8f6e9", size = 66748, upload-time = "2025-12-04T18:41:00.318Z" }, + { url = "https://files.pythonhosted.org/packages/7e/37/798c9a295cb91a0f56d86e120737e354fb7322b4c252821ee7e474e69cb3/botocore_stubs-1.42.6-py3-none-any.whl", hash = "sha256:08502fdac438b1934a803e5fd11838b2792c68115274a20f3677ded250caa7d2", size = 66749, upload-time = "2025-12-09T23:26:06.267Z" }, ] [[package]] @@ -681,59 +681,62 @@ wheels = [ [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "(implementation_name != 'PyPy' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (implementation_name != 'PyPy' and sys_platform != 'darwin' and sys_platform != 'linux')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, ] [[package]] @@ -1394,7 +1397,7 @@ scrapfly-sdk = [ ] selenium = [ { name = "selenium", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "selenium", version = "4.38.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "selenium", version = "4.39.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] serpapi = [ { name = "serpapi" }, @@ -1489,53 +1492,52 @@ provides-extras = ["apify", "beautifulsoup4", "bedrock", "browserbase", "composi [[package]] name = "cryptography" -version = "46.0.0" +version = "46.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/ee/04cd4314db26ffc951c1ea90bde30dd226880ab9343759d7abbecef377ee/cryptography-46.0.0.tar.gz", hash = "sha256:99f64a6d15f19f3afd78720ad2978f6d8d4c68cd4eb600fab82ab1a7c2071dca", size = 749158, upload-time = "2025-09-16T21:07:49.091Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/bd/3e935ca6e87dc4969683f5dd9e49adaf2cb5734253d93317b6b346e0bd33/cryptography-46.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:c9c4121f9a41cc3d02164541d986f59be31548ad355a5c96ac50703003c50fb7", size = 7285468, upload-time = "2025-09-16T21:05:52.026Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ee/dd17f412ce64b347871d7752657c5084940d42af4d9c25b1b91c7ee53362/cryptography-46.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4f70cbade61a16f5e238c4b0eb4e258d177a2fcb59aa0aae1236594f7b0ae338", size = 4308218, upload-time = "2025-09-16T21:05:55.653Z" }, - { url = "https://files.pythonhosted.org/packages/2f/53/f0b865a971e4e8b3e90e648b6f828950dea4c221bb699421e82ef45f0ef9/cryptography-46.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1eccae15d5c28c74b2bea228775c63ac5b6c36eedb574e002440c0bc28750d3", size = 4571982, upload-time = "2025-09-16T21:05:57.322Z" }, - { url = "https://files.pythonhosted.org/packages/d4/c8/035be5fd63a98284fd74df9e04156f9fed7aa45cef41feceb0d06cbdadd0/cryptography-46.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1b4fba84166d906a22027f0d958e42f3a4dbbb19c28ea71f0fb7812380b04e3c", size = 4307996, upload-time = "2025-09-16T21:05:59.043Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/dbb6d7d0a48b95984e2d4caf0a4c7d6606cea5d30241d984c0c02b47f1b6/cryptography-46.0.0-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:523153480d7575a169933f083eb47b1edd5fef45d87b026737de74ffeb300f69", size = 4015692, upload-time = "2025-09-16T21:06:01.324Z" }, - { url = "https://files.pythonhosted.org/packages/65/48/aafcffdde716f6061864e56a0a5908f08dcb8523dab436228957c8ebd5df/cryptography-46.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:f09a3a108223e319168b7557810596631a8cb864657b0c16ed7a6017f0be9433", size = 4982192, upload-time = "2025-09-16T21:06:03.367Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ab/1e73cfc181afc3054a09e5e8f7753a8fba254592ff50b735d7456d197353/cryptography-46.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c1f6ccd6f2eef3b2eb52837f0463e853501e45a916b3fc42e5d93cf244a4b97b", size = 4603944, upload-time = "2025-09-16T21:06:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/3a/02/d71dac90b77c606c90c366571edf264dc8bd37cf836e7f902253cbf5aa77/cryptography-46.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:80a548a5862d6912a45557a101092cd6c64ae1475b82cef50ee305d14a75f598", size = 4308149, upload-time = "2025-09-16T21:06:07.006Z" }, - { url = "https://files.pythonhosted.org/packages/29/e6/4dcb67fdc6addf4e319a99c4bed25776cb691f3aa6e0c4646474748816c6/cryptography-46.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6c39fd5cd9b7526afa69d64b5e5645a06e1b904f342584b3885254400b63f1b3", size = 4947449, upload-time = "2025-09-16T21:06:11.244Z" }, - { url = "https://files.pythonhosted.org/packages/26/04/91e3fad8ee33aa87815c8f25563f176a58da676c2b14757a4d3b19f0253c/cryptography-46.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d5c0cbb2fb522f7e39b59a5482a1c9c5923b7c506cfe96a1b8e7368c31617ac0", size = 4603549, upload-time = "2025-09-16T21:06:13.268Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6e/caf4efadcc8f593cbaacfbb04778f78b6d0dac287b45cec25e5054de38b7/cryptography-46.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6d8945bc120dcd90ae39aa841afddaeafc5f2e832809dc54fb906e3db829dfdc", size = 4435976, upload-time = "2025-09-16T21:06:16.514Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c0/704710f349db25c5b91965c3662d5a758011b2511408d9451126429b6cd6/cryptography-46.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:88c09da8a94ac27798f6b62de6968ac78bb94805b5d272dbcfd5fdc8c566999f", size = 4709447, upload-time = "2025-09-16T21:06:19.246Z" }, - { url = "https://files.pythonhosted.org/packages/91/5e/ff63bfd27b75adaf75cc2398de28a0b08105f9d7f8193f3b9b071e38e8b9/cryptography-46.0.0-cp311-abi3-win32.whl", hash = "sha256:3738f50215211cee1974193a1809348d33893696ce119968932ea117bcbc9b1d", size = 3058317, upload-time = "2025-09-16T21:06:21.466Z" }, - { url = "https://files.pythonhosted.org/packages/46/47/4caf35014c4551dd0b43aa6c2e250161f7ffcb9c3918c9e075785047d5d2/cryptography-46.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bbaa5eef3c19c66613317dc61e211b48d5f550db009c45e1c28b59d5a9b7812a", size = 3523891, upload-time = "2025-09-16T21:06:23.856Z" }, - { url = "https://files.pythonhosted.org/packages/98/66/6a0cafb3084a854acf808fccf756cbc9b835d1b99fb82c4a15e2e2ffb404/cryptography-46.0.0-cp311-abi3-win_arm64.whl", hash = "sha256:16b5ac72a965ec9d1e34d9417dbce235d45fa04dac28634384e3ce40dfc66495", size = 2932145, upload-time = "2025-09-16T21:06:25.842Z" }, - { url = "https://files.pythonhosted.org/packages/f2/5f/0cf967a1dc1419d5dde111bd0e22872038199f4e4655539ea6f4da5ad7f1/cryptography-46.0.0-cp314-abi3-macosx_10_9_universal2.whl", hash = "sha256:91585fc9e696abd7b3e48a463a20dda1a5c0eeeca4ba60fa4205a79527694390", size = 7203952, upload-time = "2025-09-16T21:06:28.21Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/80e7256a4677c2e9eb762638e8200a51f6dd56d2e3de3e34d0a83c2f5f80/cryptography-46.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:1d2073313324226fd846e6b5fc340ed02d43fd7478f584741bd6b791c33c9fee", size = 7257206, upload-time = "2025-09-16T21:06:59.295Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b8/a5ed987f5c11b242713076121dddfff999d81fb492149c006a579d0e4099/cryptography-46.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83af84ebe7b6e9b6de05050c79f8cc0173c864ce747b53abce6a11e940efdc0d", size = 4301182, upload-time = "2025-09-16T21:07:01.624Z" }, - { url = "https://files.pythonhosted.org/packages/da/94/f1c1f30110c05fa5247bf460b17acfd52fa3f5c77e94ba19cff8957dc5e6/cryptography-46.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c3cd09b1490c1509bf3892bde9cef729795fae4a2fee0621f19be3321beca7e4", size = 4562561, upload-time = "2025-09-16T21:07:03.386Z" }, - { url = "https://files.pythonhosted.org/packages/5d/54/8decbf2f707350bedcd525833d3a0cc0203d8b080d926ad75d5c4de701ba/cryptography-46.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d14eaf1569d6252280516bedaffdd65267428cdbc3a8c2d6de63753cf0863d5e", size = 4301974, upload-time = "2025-09-16T21:07:04.962Z" }, - { url = "https://files.pythonhosted.org/packages/82/63/c34a2f3516c6b05801f129616a5a1c68a8c403b91f23f9db783ee1d4f700/cryptography-46.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ab3a14cecc741c8c03ad0ad46dfbf18de25218551931a23bca2731d46c706d83", size = 4009462, upload-time = "2025-09-16T21:07:06.569Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c5/92ef920a4cf8ff35fcf9da5a09f008a6977dcb9801c709799ec1bf2873fb/cryptography-46.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8e8b222eb54e3e7d3743a7c2b1f7fa7df7a9add790307bb34327c88ec85fe087", size = 4980769, upload-time = "2025-09-16T21:07:08.269Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8f/1705f7ea3b9468c4a4fef6cce631db14feb6748499870a4772993cbeb729/cryptography-46.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7f3f88df0c9b248dcc2e76124f9140621aca187ccc396b87bc363f890acf3a30", size = 4591812, upload-time = "2025-09-16T21:07:10.288Z" }, - { url = "https://files.pythonhosted.org/packages/34/b9/2d797ce9d346b8bac9f570b43e6e14226ff0f625f7f6f2f95d9065e316e3/cryptography-46.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9aa85222f03fdb30defabc7a9e1e3d4ec76eb74ea9fe1504b2800844f9c98440", size = 4301844, upload-time = "2025-09-16T21:07:12.522Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/8efc9712997b46aea2ac8f74adc31f780ac4662e3b107ecad0d5c1a0c7f8/cryptography-46.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f9aaf2a91302e1490c068d2f3af7df4137ac2b36600f5bd26e53d9ec320412d3", size = 4943257, upload-time = "2025-09-16T21:07:14.289Z" }, - { url = "https://files.pythonhosted.org/packages/c4/0c/bc365287a97d28aa7feef8810884831b2a38a8dc4cf0f8d6927ad1568d27/cryptography-46.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:32670ca085150ff36b438c17f2dfc54146fe4a074ebf0a76d72fb1b419a974bc", size = 4591154, upload-time = "2025-09-16T21:07:16.271Z" }, - { url = "https://files.pythonhosted.org/packages/51/3b/0b15107277b0c558c02027da615f4e78c892f22c6a04d29c6ad43fcddca6/cryptography-46.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0f58183453032727a65e6605240e7a3824fd1d6a7e75d2b537e280286ab79a52", size = 4428200, upload-time = "2025-09-16T21:07:18.118Z" }, - { url = "https://files.pythonhosted.org/packages/cf/24/814d69418247ea2cfc985eec6678239013500d745bc7a0a35a32c2e2f3be/cryptography-46.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4bc257c2d5d865ed37d0bd7c500baa71f939a7952c424f28632298d80ccd5ec1", size = 4699862, upload-time = "2025-09-16T21:07:20.219Z" }, - { url = "https://files.pythonhosted.org/packages/fb/1e/665c718e0c45281a4e22454fa8a9bd8835f1ceb667b9ffe807baa41cd681/cryptography-46.0.0-cp38-abi3-win32.whl", hash = "sha256:df932ac70388be034b2e046e34d636245d5eeb8140db24a6b4c2268cd2073270", size = 3043766, upload-time = "2025-09-16T21:07:21.969Z" }, - { url = "https://files.pythonhosted.org/packages/78/7e/12e1e13abff381c702697845d1cf372939957735f49ef66f2061f38da32f/cryptography-46.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:274f8b2eb3616709f437326185eb563eb4e5813d01ebe2029b61bfe7d9995fbb", size = 3517216, upload-time = "2025-09-16T21:07:24.024Z" }, - { url = "https://files.pythonhosted.org/packages/ad/55/009497b2ae7375db090b41f9fe7a1a7362f804ddfe17ed9e34f748fcb0e5/cryptography-46.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:249c41f2bbfa026615e7bdca47e4a66135baa81b08509ab240a2e666f6af5966", size = 2923145, upload-time = "2025-09-16T21:07:25.74Z" }, - { url = "https://files.pythonhosted.org/packages/61/d0/367ff74316d94fbe273e49f441b111a88daa8945a10baf2cd2d35f4e7077/cryptography-46.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fe9ff1139b2b1f59a5a0b538bbd950f8660a39624bbe10cf3640d17574f973bb", size = 3715000, upload-time = "2025-09-16T21:07:27.831Z" }, - { url = "https://files.pythonhosted.org/packages/9c/c7/43f68f1fe9363268e34d1026e3f3f99f0ed0f632a49a8867187161215be0/cryptography-46.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:77e3bd53c9c189cea361bc18ceb173959f8b2dd8f8d984ae118e9ac641410252", size = 3443876, upload-time = "2025-09-16T21:07:30.695Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c9/fd0ac99ac18eaa8766800bf7d087e8c011889aa6643006cff9cbd523eadd/cryptography-46.0.0-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:75d2ddde8f1766ab2db48ed7f2aa3797aeb491ea8dfe9b4c074201aec00f5c16", size = 3722472, upload-time = "2025-09-16T21:07:32.619Z" }, - { url = "https://files.pythonhosted.org/packages/f5/69/ff831514209e68a7e32fef655abfd9ef9ee4608d151636fa11eb8d7e589a/cryptography-46.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f9f85d9cf88e3ba2b2b6da3c2310d1cf75bdf04a5bc1a2e972603054f82c4dd5", size = 4249520, upload-time = "2025-09-16T21:07:34.409Z" }, - { url = "https://files.pythonhosted.org/packages/19/4a/19960010da2865f521a5bd657eaf647d6a4368568e96f6d9ec635e47ad55/cryptography-46.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:834af45296083d892e23430e3b11df77e2ac5c042caede1da29c9bf59016f4d2", size = 4528031, upload-time = "2025-09-16T21:07:36.721Z" }, - { url = "https://files.pythonhosted.org/packages/79/92/88970c2b5b270d232213a971e74afa6d0e82d8aeee0964765a78ee1f55c8/cryptography-46.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:c39f0947d50f74b1b3523cec3931315072646286fb462995eb998f8136779319", size = 4249072, upload-time = "2025-09-16T21:07:38.382Z" }, - { url = "https://files.pythonhosted.org/packages/63/50/b0b90a269d64b479602d948f40ef6131f3704546ce003baa11405aa4093b/cryptography-46.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:6460866a92143a24e3ed68eaeb6e98d0cedd85d7d9a8ab1fc293ec91850b1b38", size = 4527173, upload-time = "2025-09-16T21:07:40.742Z" }, - { url = "https://files.pythonhosted.org/packages/37/e1/826091488f6402c904e831ccbde41cf1a08672644ee5107e2447ea76a903/cryptography-46.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bf1961037309ee0bdf874ccba9820b1c2f720c2016895c44d8eb2316226c1ad5", size = 3448199, upload-time = "2025-09-16T21:07:42.639Z" }, + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" }, + { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" }, + { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" }, + { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" }, + { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" }, + { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" }, ] [[package]] @@ -1669,7 +1671,7 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "requests" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } wheels = [ @@ -1718,7 +1720,7 @@ wheels = [ [[package]] name = "docling-core" -version = "2.54.0" +version = "2.54.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonref" }, @@ -1732,9 +1734,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/26/ee72224776034a85cb167d8d076bf206e423234d3e34481362edab0c966d/docling_core-2.54.0.tar.gz", hash = "sha256:a8091215019a259fdce5c8ecc80d939aed6350222054e382f009e5e437066408", size = 200138, upload-time = "2025-11-29T06:16:13.076Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/9b/0804689b01667def131e57efd38a8cf4cbab847cd4db993068dfe75a3488/docling_core-2.54.1.tar.gz", hash = "sha256:c6595e8b84f5e9de7a0a6298206d749f4be5730824604e684f204078a3689985", size = 200260, upload-time = "2025-12-08T12:58:39.407Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/7c/639141d6b976948e4164e4306f9b9cdc7e7421ab3180ff996c86c93232d4/docling_core-2.54.0-py3-none-any.whl", hash = "sha256:8246412c93d37e897ec1a29e5a897674032fed8084b277fb6e5fdc595cfe4eaa", size = 200164, upload-time = "2025-11-29T06:16:11.634Z" }, + { url = "https://files.pythonhosted.org/packages/40/e6/1e44ca753e0808bf5fa8782dfa3bbb7dbd54eb01a223f426a650a5361869/docling_core-2.54.1-py3-none-any.whl", hash = "sha256:2d22ee1cd8508fa7ffdd2e01ef30d7521390ff55ef240610f16be0cf54ffe73e", size = 200232, upload-time = "2025-12-08T12:58:37.914Z" }, ] [package.optional-dependencies] @@ -1920,7 +1922,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.123.8" +version = "0.124.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1928,9 +1930,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/99/8f2d4be9af90b3e56b865a07bdd390398e53d67c9c95c729b5772e528179/fastapi-0.123.8.tar.gz", hash = "sha256:d106de125c8dd3d4341517fa2ae36d9cffe82a6500bd910d3c080e6c42b1b490", size = 354253, upload-time = "2025-12-04T13:02:54.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/9c/11969bd3e3bc4aa3a711f83dd3720239d3565a934929c74fc32f6c9f3638/fastapi-0.124.0.tar.gz", hash = "sha256:260cd178ad75e6d259991f2fd9b0fee924b224850079df576a3ba604ce58f4e6", size = 357623, upload-time = "2025-12-06T13:11:35.692Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/23/dd53f49e8309454e2c52bdfffe7493cc0f00d10e2fc885d3f4d64c90731f/fastapi-0.123.8-py3-none-any.whl", hash = "sha256:d7c8db95f61d398f7e1491ad52e6b2362755f8ec61c7a740b29e70f18a2901e3", size = 111645, upload-time = "2025-12-04T13:02:53.163Z" }, + { url = "https://files.pythonhosted.org/packages/4d/29/9e1e82e16e9a1763d3b55bfbe9b2fa39d7175a1fd97685c482fa402e111d/fastapi-0.124.0-py3-none-any.whl", hash = "sha256:91596bdc6dde303c318f06e8d2bc75eafb341fc793a0c9c92c0bc1db1ac52480", size = 112505, upload-time = "2025-12-06T13:11:34.392Z" }, ] [[package]] @@ -1955,6 +1957,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/38/447aabefddda026c3b65b3b9f1fec48ab78b648441e3e530bf8d78b26bdf/fastembed-0.7.3-py3-none-any.whl", hash = "sha256:a377b57843abd773318042960be39f1aef29827530acb98b035a554742a85cdf", size = 105322, upload-time = "2025-08-29T11:19:45.4Z" }, ] +[[package]] +name = "ffmpeg-python" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "future" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/5e/d5f9105d59c1325759d838af4e973695081fbbc97182baf73afc78dec266/ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127", size = 21543, upload-time = "2019-07-06T00:19:08.989Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/0c/56be52741f75bad4dc6555991fabd2e07b432d333da82c11ad701123888a/ffmpeg_python-0.2.0-py3-none-any.whl", hash = "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5", size = 25024, upload-time = "2019-07-06T00:19:07.215Z" }, +] + [[package]] name = "filelock" version = "3.20.0" @@ -1975,7 +1989,7 @@ wheels = [ [[package]] name = "firecrawl-py" -version = "4.10.1" +version = "4.10.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1986,9 +2000,9 @@ dependencies = [ { name = "requests" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/c1/f84de90f3536527de04caf6e364acc67c3291c366c80538bc90b484dc88e/firecrawl_py-4.10.1.tar.gz", hash = "sha256:c373b07ee5facef1c833ed913a7e462c63fee66a5eac9212f7b44a9b888520e7", size = 153690, upload-time = "2025-12-03T18:26:22.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/c2/c60f705a789d1e1d6b1d81759ae1df9869e803765a2fca4ab4b5cd7c4300/firecrawl_py-4.10.4.tar.gz", hash = "sha256:26bc3cc81d14348379fb24fca7f3c186d76f132a38e72a3096f1402b4c33985a", size = 154561, upload-time = "2025-12-09T18:10:21.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/ce/1ab9c7c8d56e44840dba597510d76ac3eb15f496c817f6c745f5e6423059/firecrawl_py-4.10.1-py3-none-any.whl", hash = "sha256:cf71788c2c787e83f5eed566e47a830c2ec1323dcd1c2817ae37c123e365b48e", size = 191221, upload-time = "2025-12-03T18:26:20.913Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/f8514cffd0d59ff8f47f5dac8929ddb095ff0b07dfea0a8348c469d522b5/firecrawl_py-4.10.4-py3-none-any.whl", hash = "sha256:05f5223b6655176e3d082035bbf514c56388696fe0a52a9e884f6937ed21f2a2", size = 192343, upload-time = "2025-12-09T18:10:19.693Z" }, ] [[package]] @@ -2139,6 +2153,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/51/c7/b64cae5dba3a1b138d7123ec36bb5ccd39d39939f18454407e5468f4763f/fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b", size = 201422, upload-time = "2025-12-03T15:23:41.434Z" }, ] +[[package]] +name = "future" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490, upload-time = "2024-02-21T11:52:38.461Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326, upload-time = "2024-02-21T11:52:35.956Z" }, +] + [[package]] name = "gitdb" version = "4.0.12" @@ -2659,7 +2682,7 @@ dependencies = [ { name = "jmespath", marker = "platform_python_implementation != 'PyPy'" }, { name = "python-dateutil", marker = "platform_python_implementation != 'PyPy'" }, { name = "requests", marker = "platform_python_implementation != 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/45/80c23aa1e13175a9deefe43cbf8e853a3d3bfc8dfa8b6d6fe83e5785fe21/ibm_cos_sdk_core-2.14.3.tar.gz", hash = "sha256:85dee7790c92e8db69bf39dae4c02cac211e3c1d81bb86e64fa2d1e929674623", size = 1103637, upload-time = "2025-08-01T06:35:41.645Z" } @@ -2725,7 +2748,7 @@ dependencies = [ { name = "requests" }, { name = "tabulate" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/56/2e3df38a1f13062095d7bde23c87a92f3898982993a15186b1bfecbd206f/ibm_watsonx_ai-1.3.42.tar.gz", hash = "sha256:ee5be59009004245d957ce97d1227355516df95a2640189749487614fef674ff", size = 688651, upload-time = "2025-10-01T13:35:41.527Z" } wheels = [ @@ -3215,7 +3238,7 @@ dependencies = [ { name = "requests-oauthlib" }, { name = "six" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, { name = "websocket-client" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/52/19ebe8004c243fdfa78268a96727c71e08f00ff6fe69a301d0b7fcbce3c2/kubernetes-33.1.0.tar.gz", hash = "sha256:f64d829843a54c251061a8e7a14523b521f2dc5c896cf6d65ccf348648a88993", size = 1036779, upload-time = "2025-06-09T21:57:58.521Z" } @@ -3302,7 +3325,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2 [[package]] name = "langsmith" -version = "0.4.53" +version = "0.4.57" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3314,9 +3337,9 @@ dependencies = [ { name = "uuid-utils" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/1c/8c4fbb995d176594d79e7347ca5e3cf1a839d300bee2b6b38861fbf57809/langsmith-0.4.53.tar.gz", hash = "sha256:362255850ac80abf6edc9e9b3455c42aa248e12686a24c637d4c56fc41139ffe", size = 990765, upload-time = "2025-12-03T01:00:43.505Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/18/fbd779dd73bae0389f8dde1de02299e9cda7ee59db56dcadb761a8b22cec/langsmith-0.4.57.tar.gz", hash = "sha256:c6e55266ec82c559517359cfbd82c70ea6c216b48f873ba9b268896bde381e35", size = 991971, upload-time = "2025-12-09T21:53:53.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/48/37cc533e2d16e4ec1d01f30b41933c9319af18389ea0f6866835ace7d331/langsmith-0.4.53-py3-none-any.whl", hash = "sha256:62e0b69d0f3b25afbd63dc5743a3bcec52993fe6c4e43e5b9e5311606aa04e9e", size = 411526, upload-time = "2025-12-03T01:00:42.053Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d7/fa4c9f79cd2193ee32dc8246e781c153cc84bb06221d3f53e9ce46dc9375/langsmith-0.4.57-py3-none-any.whl", hash = "sha256:a49ce6aebcda472b03a9e5e441091bb47d8a422f9e5c777cbcddff5fe72bcbf9", size = 412577, upload-time = "2025-12-09T21:53:51.518Z" }, ] [[package]] @@ -3330,53 +3353,53 @@ wheels = [ [[package]] name = "librt" -version = "0.6.3" +version = "0.7.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/c3/cdff3c10e2e608490dc0a310ccf11ba777b3943ad4fcead2a2ade98c21e1/librt-0.6.3.tar.gz", hash = "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae", size = 54209, upload-time = "2025-11-29T14:01:56.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/d9/6f3d3fcf5e5543ed8a60cc70fa7d50508ed60b8a10e9af6d2058159ab54e/librt-0.7.3.tar.gz", hash = "sha256:3ec50cf65235ff5c02c5b747748d9222e564ad48597122a361269dd3aa808798", size = 144549, upload-time = "2025-12-06T19:04:45.553Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/84/859df8db21dedab2538ddfbe1d486dda3eb66a98c6ad7ba754a99e25e45e/librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1", size = 27294, upload-time = "2025-11-29T14:00:35.053Z" }, - { url = "https://files.pythonhosted.org/packages/f7/01/ec3971cf9c4f827f17de6729bdfdbf01a67493147334f4ef8fac68936e3a/librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf", size = 27635, upload-time = "2025-11-29T14:00:36.496Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f9/3efe201df84dd26388d2e0afa4c4dc668c8e406a3da7b7319152faf835a1/librt-0.6.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437", size = 81768, upload-time = "2025-11-29T14:00:37.451Z" }, - { url = "https://files.pythonhosted.org/packages/0a/13/f63e60bc219b17f3d8f3d13423cd4972e597b0321c51cac7bfbdd5e1f7b9/librt-0.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089", size = 85884, upload-time = "2025-11-29T14:00:38.433Z" }, - { url = "https://files.pythonhosted.org/packages/c2/42/0068f14f39a79d1ce8a19d4988dd07371df1d0a7d3395fbdc8a25b1c9437/librt-0.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513", size = 85830, upload-time = "2025-11-29T14:00:39.418Z" }, - { url = "https://files.pythonhosted.org/packages/14/1c/87f5af3a9e6564f09e50c72f82fc3057fd42d1facc8b510a707d0438c4ad/librt-0.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1", size = 88086, upload-time = "2025-11-29T14:00:40.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/22153b98b88a913b5b3f266f12e57df50a2a6960b3f8fcb825b1a0cfe40a/librt-0.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977", size = 86470, upload-time = "2025-11-29T14:00:41.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/3c/ea1edb587799b1edcc22444e0630fa422e32d7aaa5bfb5115b948acc2d1c/librt-0.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d", size = 89079, upload-time = "2025-11-29T14:00:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/73/ad/50bb4ae6b07c9f3ab19653e0830a210533b30eb9a18d515efb5a2b9d0c7c/librt-0.6.3-cp310-cp310-win32.whl", hash = "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46", size = 19820, upload-time = "2025-11-29T14:00:44.211Z" }, - { url = "https://files.pythonhosted.org/packages/7a/12/7426ee78f3b1dbe11a90619d54cb241ca924ca3c0ff9ade3992178e9b440/librt-0.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457", size = 21332, upload-time = "2025-11-29T14:00:45.427Z" }, - { url = "https://files.pythonhosted.org/packages/8b/80/bc60fd16fe24910bf5974fb914778a2e8540cef55385ab2cb04a0dfe42c4/librt-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08", size = 27285, upload-time = "2025-11-29T14:00:46.626Z" }, - { url = "https://files.pythonhosted.org/packages/88/3c/26335536ed9ba097c79cffcee148393592e55758fe76d99015af3e47a6d0/librt-0.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa", size = 27629, upload-time = "2025-11-29T14:00:47.863Z" }, - { url = "https://files.pythonhosted.org/packages/af/fd/2dcedeacfedee5d2eda23e7a49c1c12ce6221b5d58a13555f053203faafc/librt-0.6.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122", size = 82039, upload-time = "2025-11-29T14:00:49.131Z" }, - { url = "https://files.pythonhosted.org/packages/48/ff/6aa11914b83b0dc2d489f7636942a8e3322650d0dba840db9a1b455f3caa/librt-0.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f", size = 86560, upload-time = "2025-11-29T14:00:50.403Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/d25af61958c2c7eb978164aeba0350719f615179ba3f428b682b9a5fdace/librt-0.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040", size = 86494, upload-time = "2025-11-29T14:00:51.383Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4b/40e75d3b258c801908e64b39788f9491635f9554f8717430a491385bd6f2/librt-0.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523", size = 88914, upload-time = "2025-11-29T14:00:52.688Z" }, - { url = "https://files.pythonhosted.org/packages/97/6d/0070c81aba8a169224301c75fb5fb6c3c25ca67e6ced086584fc130d5a67/librt-0.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f", size = 86944, upload-time = "2025-11-29T14:00:53.768Z" }, - { url = "https://files.pythonhosted.org/packages/a6/94/809f38887941b7726692e0b5a083dbdc87dbb8cf893e3b286550c5f0b129/librt-0.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47", size = 89852, upload-time = "2025-11-29T14:00:54.71Z" }, - { url = "https://files.pythonhosted.org/packages/58/a3/b0e5b1cda675b91f1111d8ba941da455d8bfaa22f4d2d8963ba96ccb5b12/librt-0.6.3-cp311-cp311-win32.whl", hash = "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7", size = 19948, upload-time = "2025-11-29T14:00:55.989Z" }, - { url = "https://files.pythonhosted.org/packages/cc/73/70011c2b37e3be3ece3affd3abc8ebe5cda482b03fd6b3397906321a901e/librt-0.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8", size = 21406, upload-time = "2025-11-29T14:00:56.874Z" }, - { url = "https://files.pythonhosted.org/packages/91/ee/119aa759290af6ca0729edf513ca390c1afbeae60f3ecae9b9d56f25a8a9/librt-0.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45", size = 20875, upload-time = "2025-11-29T14:00:57.752Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2c/b59249c566f98fe90e178baf59e83f628d6c38fb8bc78319301fccda0b5e/librt-0.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176", size = 27841, upload-time = "2025-11-29T14:00:58.925Z" }, - { url = "https://files.pythonhosted.org/packages/40/e8/9db01cafcd1a2872b76114c858f81cc29ce7ad606bc102020d6dabf470fb/librt-0.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057", size = 27844, upload-time = "2025-11-29T14:01:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/59/4d/da449d3a7d83cc853af539dee42adc37b755d7eea4ad3880bacfd84b651d/librt-0.6.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610", size = 84091, upload-time = "2025-11-29T14:01:01.118Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6c/f90306906fb6cc6eaf4725870f0347115de05431e1f96d35114392d31fda/librt-0.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c", size = 88239, upload-time = "2025-11-29T14:01:02.11Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ae/473ce7b423cfac2cb503851a89d9d2195bf615f534d5912bf86feeebbee7/librt-0.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9", size = 88815, upload-time = "2025-11-29T14:01:03.114Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6d/934df738c87fb9617cabefe4891eece585a06abe6def25b4bca3b174429d/librt-0.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c", size = 90598, upload-time = "2025-11-29T14:01:04.071Z" }, - { url = "https://files.pythonhosted.org/packages/72/89/eeaa124f5e0f431c2b39119550378ae817a4b1a3c93fd7122f0639336fff/librt-0.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c", size = 88603, upload-time = "2025-11-29T14:01:05.02Z" }, - { url = "https://files.pythonhosted.org/packages/4d/ed/c60b3c1cfc27d709bc0288af428ce58543fcb5053cf3eadbc773c24257f5/librt-0.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf", size = 92112, upload-time = "2025-11-29T14:01:06.304Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ab/f56169be5f716ef4ab0277be70bcb1874b4effc262e655d85b505af4884d/librt-0.6.3-cp312-cp312-win32.whl", hash = "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d", size = 20127, upload-time = "2025-11-29T14:01:07.283Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8d/222750ce82bf95125529eaab585ac7e2829df252f3cfc05d68792fb1dd2c/librt-0.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01", size = 21545, upload-time = "2025-11-29T14:01:08.184Z" }, - { url = "https://files.pythonhosted.org/packages/72/c9/f731ddcfb72f446a92a8674c6b8e1e2242773cce43a04f41549bd8b958ff/librt-0.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55", size = 20946, upload-time = "2025-11-29T14:01:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/dd/aa/3055dd440f8b8b3b7e8624539a0749dd8e1913e978993bcca9ce7e306231/librt-0.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3", size = 27874, upload-time = "2025-11-29T14:01:10.615Z" }, - { url = "https://files.pythonhosted.org/packages/ef/93/226d7dd455eaa4c26712b5ccb2dfcca12831baa7f898c8ffd3a831e29fda/librt-0.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5", size = 27852, upload-time = "2025-11-29T14:01:11.535Z" }, - { url = "https://files.pythonhosted.org/packages/4e/8b/db9d51191aef4e4cc06285250affe0bb0ad8b2ed815f7ca77951655e6f02/librt-0.6.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42", size = 84264, upload-time = "2025-11-29T14:01:12.461Z" }, - { url = "https://files.pythonhosted.org/packages/8d/53/297c96bda3b5a73bdaf748f1e3ae757edd29a0a41a956b9c10379f193417/librt-0.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70", size = 88432, upload-time = "2025-11-29T14:01:13.405Z" }, - { url = "https://files.pythonhosted.org/packages/54/3a/c005516071123278e340f22de72fa53d51e259d49215295c212da16c4dc2/librt-0.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522", size = 89014, upload-time = "2025-11-29T14:01:14.373Z" }, - { url = "https://files.pythonhosted.org/packages/8e/9b/ea715f818d926d17b94c80a12d81a79e95c44f52848e61e8ca1ff29bb9a9/librt-0.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30", size = 90807, upload-time = "2025-11-29T14:01:15.377Z" }, - { url = "https://files.pythonhosted.org/packages/f0/fc/4e2e4c87e002fa60917a8e474fd13c4bac9a759df82be3778573bb1ab954/librt-0.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917", size = 88890, upload-time = "2025-11-29T14:01:16.633Z" }, - { url = "https://files.pythonhosted.org/packages/70/7f/c7428734fbdfd4db3d5b9237fc3a857880b2ace66492836f6529fef25d92/librt-0.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530", size = 92300, upload-time = "2025-11-29T14:01:17.658Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0c/738c4824fdfe74dc0f95d5e90ef9e759d4ecf7fd5ba964d54a7703322251/librt-0.6.3-cp313-cp313-win32.whl", hash = "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3", size = 20159, upload-time = "2025-11-29T14:01:18.61Z" }, - { url = "https://files.pythonhosted.org/packages/f2/95/93d0e61bc617306ecf4c54636b5cbde4947d872563565c4abdd9d07a39d3/librt-0.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88", size = 21484, upload-time = "2025-11-29T14:01:19.506Z" }, - { url = "https://files.pythonhosted.org/packages/10/23/abd7ace79ab54d1dbee265f13529266f686a7ce2d21ab59a992f989009b6/librt-0.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9", size = 20935, upload-time = "2025-11-29T14:01:20.415Z" }, + { url = "https://files.pythonhosted.org/packages/4d/66/79a14e672256ef58144a24eb49adb338ec02de67ff4b45320af6504682ab/librt-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2682162855a708e3270eba4b92026b93f8257c3e65278b456c77631faf0f4f7a", size = 54707, upload-time = "2025-12-06T19:03:10.881Z" }, + { url = "https://files.pythonhosted.org/packages/58/fa/b709c65a9d5eab85f7bcfe0414504d9775aaad6e78727a0327e175474caa/librt-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:440c788f707c061d237c1e83edf6164ff19f5c0f823a3bf054e88804ebf971ec", size = 56670, upload-time = "2025-12-06T19:03:12.107Z" }, + { url = "https://files.pythonhosted.org/packages/3a/56/0685a0772ec89ddad4c00e6b584603274c3d818f9a68e2c43c4eb7b39ee9/librt-0.7.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399938edbd3d78339f797d685142dd8a623dfaded023cf451033c85955e4838a", size = 161045, upload-time = "2025-12-06T19:03:13.444Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d9/863ada0c5ce48aefb89df1555e392b2209fcb6daee4c153c031339b9a89b/librt-0.7.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1975eda520957c6e0eb52d12968dd3609ffb7eef05d4223d097893d6daf1d8a7", size = 169532, upload-time = "2025-12-06T19:03:14.699Z" }, + { url = "https://files.pythonhosted.org/packages/68/a0/71da6c8724fd16c31749905ef1c9e11de206d9301b5be984bf2682b4efb3/librt-0.7.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9da128d0edf990cf0d2ca011b02cd6f639e79286774bd5b0351245cbb5a6e51", size = 183277, upload-time = "2025-12-06T19:03:16.446Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/9c97bf2f8338ba1914de233ea312bba2bbd7c59f43f807b3e119796bab18/librt-0.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19acfde38cb532a560b98f473adc741c941b7a9bc90f7294bc273d08becb58b", size = 179045, upload-time = "2025-12-06T19:03:17.838Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b1/ceea067f489e904cb4ddcca3c9b06ba20229bc3fa7458711e24a5811f162/librt-0.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b4f57f7a0c65821c5441d98c47ff7c01d359b1e12328219709bdd97fdd37f90", size = 173521, upload-time = "2025-12-06T19:03:19.17Z" }, + { url = "https://files.pythonhosted.org/packages/7a/41/6cb18f5da9c89ed087417abb0127a445a50ad4eaf1282ba5b52588187f47/librt-0.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:256793988bff98040de23c57cf36e1f4c2f2dc3dcd17537cdac031d3b681db71", size = 193592, upload-time = "2025-12-06T19:03:20.637Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3c/fcef208746584e7c78584b7aedc617130c4a4742cb8273361bbda8b183b5/librt-0.7.3-cp310-cp310-win32.whl", hash = "sha256:fcb72249ac4ea81a7baefcbff74df7029c3cb1cf01a711113fa052d563639c9c", size = 47201, upload-time = "2025-12-06T19:03:21.764Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bf/d8a6c35d1b2b789a4df9b3ddb1c8f535ea373fde2089698965a8f0d62138/librt-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4887c29cadbdc50640179e3861c276325ff2986791e6044f73136e6e798ff806", size = 54371, upload-time = "2025-12-06T19:03:23.231Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/f6391f5c6f158d31ed9af6bd1b1bcd3ffafdea1d816bc4219d0d90175a7f/librt-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:687403cced6a29590e6be6964463835315905221d797bc5c934a98750fe1a9af", size = 54711, upload-time = "2025-12-06T19:03:24.6Z" }, + { url = "https://files.pythonhosted.org/packages/ab/1b/53c208188c178987c081560a0fcf36f5ca500d5e21769596c845ef2f40d4/librt-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24d70810f6e2ea853ff79338001533716b373cc0f63e2a0be5bc96129edb5fb5", size = 56664, upload-time = "2025-12-06T19:03:25.969Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5c/d9da832b9a1e5f8366e8a044ec80217945385b26cb89fd6f94bfdc7d80b0/librt-0.7.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf8c7735fbfc0754111f00edda35cf9e98a8d478de6c47b04eaa9cef4300eaa7", size = 161701, upload-time = "2025-12-06T19:03:27.035Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/1e0a7aba15e78529dd21f233076b876ee58c8b8711b1793315bdd3b263b0/librt-0.7.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32d43610dff472eab939f4d7fbdd240d1667794192690433672ae22d7af8445", size = 171040, upload-time = "2025-12-06T19:03:28.482Z" }, + { url = "https://files.pythonhosted.org/packages/69/46/3cfa325c1c2bc25775ec6ec1718cfbec9cff4ac767d37d2d3a2d1cc6f02c/librt-0.7.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:adeaa886d607fb02563c1f625cf2ee58778a2567c0c109378da8f17ec3076ad7", size = 184720, upload-time = "2025-12-06T19:03:29.599Z" }, + { url = "https://files.pythonhosted.org/packages/99/bb/e4553433d7ac47f4c75d0a7e59b13aee0e08e88ceadbee356527a9629b0a/librt-0.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572a24fc5958c61431da456a0ef1eeea6b4989d81eeb18b8e5f1f3077592200b", size = 180731, upload-time = "2025-12-06T19:03:31.201Z" }, + { url = "https://files.pythonhosted.org/packages/35/89/51cd73006232981a3106d4081fbaa584ac4e27b49bc02266468d3919db03/librt-0.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6488e69d408b492e08bfb68f20c4a899a354b4386a446ecd490baff8d0862720", size = 174565, upload-time = "2025-12-06T19:03:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/42/54/0578a78b587e5aa22486af34239a052c6366835b55fc307bc64380229e3f/librt-0.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed028fc3d41adda916320712838aec289956c89b4f0a361ceadf83a53b4c047a", size = 195247, upload-time = "2025-12-06T19:03:34.434Z" }, + { url = "https://files.pythonhosted.org/packages/b5/0a/ee747cd999753dd9447e50b98fc36ee433b6c841a42dbf6d47b64b32a56e/librt-0.7.3-cp311-cp311-win32.whl", hash = "sha256:2cf9d73499486ce39eebbff5f42452518cc1f88d8b7ea4a711ab32962b176ee2", size = 47514, upload-time = "2025-12-06T19:03:35.959Z" }, + { url = "https://files.pythonhosted.org/packages/ec/af/8b13845178dec488e752878f8e290f8f89e7e34ae1528b70277aa1a6dd1e/librt-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:35f1609e3484a649bb80431310ddbec81114cd86648f1d9482bc72a3b86ded2e", size = 54695, upload-time = "2025-12-06T19:03:36.956Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/ae59578501b1a25850266778f59279f4f3e726acc5c44255bfcb07b4bc57/librt-0.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:550fdbfbf5bba6a2960b27376ca76d6aaa2bd4b1a06c4255edd8520c306fcfc0", size = 48142, upload-time = "2025-12-06T19:03:38.263Z" }, + { url = "https://files.pythonhosted.org/packages/29/90/ed8595fa4e35b6020317b5ea8d226a782dcbac7a997c19ae89fb07a41c66/librt-0.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fa9ac2e49a6bee56e47573a6786cb635e128a7b12a0dc7851090037c0d397a3", size = 55687, upload-time = "2025-12-06T19:03:39.245Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f6/6a20702a07b41006cb001a759440cb6b5362530920978f64a2b2ae2bf729/librt-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e980cf1ed1a2420a6424e2ed884629cdead291686f1048810a817de07b5eb18", size = 57127, upload-time = "2025-12-06T19:03:40.3Z" }, + { url = "https://files.pythonhosted.org/packages/79/f3/b0c4703d5ffe9359b67bb2ccb86c42d4e930a363cfc72262ac3ba53cff3e/librt-0.7.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e094e445c37c57e9ec612847812c301840239d34ccc5d153a982fa9814478c60", size = 165336, upload-time = "2025-12-06T19:03:41.369Z" }, + { url = "https://files.pythonhosted.org/packages/02/69/3ba05b73ab29ccbe003856232cea4049769be5942d799e628d1470ed1694/librt-0.7.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca73d70c3f553552ba9133d4a09e767dcfeee352d8d8d3eb3f77e38a3beb3ed", size = 174237, upload-time = "2025-12-06T19:03:42.44Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/d7c2671e7bf6c285ef408aa435e9cd3fdc06fd994601e1f2b242df12034f/librt-0.7.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c634a0a6db395fdaba0361aa78395597ee72c3aad651b9a307a3a7eaf5efd67e", size = 189017, upload-time = "2025-12-06T19:03:44.01Z" }, + { url = "https://files.pythonhosted.org/packages/f4/94/d13f57193148004592b618555f296b41d2d79b1dc814ff8b3273a0bf1546/librt-0.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a59a69deeb458c858b8fea6acf9e2acd5d755d76cd81a655256bc65c20dfff5b", size = 183983, upload-time = "2025-12-06T19:03:45.834Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/b612a9944ebd39fa143c7e2e2d33f2cb790205e025ddd903fb509a3a3bb3/librt-0.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d91e60ac44bbe3a77a67af4a4c13114cbe9f6d540337ce22f2c9eaf7454ca71f", size = 177602, upload-time = "2025-12-06T19:03:46.944Z" }, + { url = "https://files.pythonhosted.org/packages/1f/48/77bc05c4cc232efae6c5592c0095034390992edbd5bae8d6cf1263bb7157/librt-0.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:703456146dc2bf430f7832fd1341adac5c893ec3c1430194fdcefba00012555c", size = 199282, upload-time = "2025-12-06T19:03:48.069Z" }, + { url = "https://files.pythonhosted.org/packages/12/aa/05916ccd864227db1ffec2a303ae34f385c6b22d4e7ce9f07054dbcf083c/librt-0.7.3-cp312-cp312-win32.whl", hash = "sha256:b7c1239b64b70be7759554ad1a86288220bbb04d68518b527783c4ad3fb4f80b", size = 47879, upload-time = "2025-12-06T19:03:49.289Z" }, + { url = "https://files.pythonhosted.org/packages/50/92/7f41c42d31ea818b3c4b9cc1562e9714bac3c676dd18f6d5dd3d0f2aa179/librt-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef59c938f72bdbc6ab52dc50f81d0637fde0f194b02d636987cea2ab30f8f55a", size = 54972, upload-time = "2025-12-06T19:03:50.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/dc/53582bbfb422311afcbc92adb75711f04e989cec052f08ec0152fbc36c9c/librt-0.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:ff21c554304e8226bf80c3a7754be27c6c3549a9fec563a03c06ee8f494da8fc", size = 48338, upload-time = "2025-12-06T19:03:51.431Z" }, + { url = "https://files.pythonhosted.org/packages/93/7d/e0ce1837dfb452427db556e6d4c5301ba3b22fe8de318379fbd0593759b9/librt-0.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56f2a47beda8409061bc1c865bef2d4bd9ff9255219402c0817e68ab5ad89aed", size = 55742, upload-time = "2025-12-06T19:03:52.459Z" }, + { url = "https://files.pythonhosted.org/packages/be/c0/3564262301e507e1d5cf31c7d84cb12addf0d35e05ba53312494a2eba9a4/librt-0.7.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14569ac5dd38cfccf0a14597a88038fb16811a6fede25c67b79c6d50fc2c8fdc", size = 57163, upload-time = "2025-12-06T19:03:53.516Z" }, + { url = "https://files.pythonhosted.org/packages/be/ac/245e72b7e443d24a562f6047563c7f59833384053073ef9410476f68505b/librt-0.7.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6038ccbd5968325a5d6fd393cf6e00b622a8de545f0994b89dd0f748dcf3e19e", size = 165840, upload-time = "2025-12-06T19:03:54.918Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/587e4491f40adba066ba39a450c66bad794c8d92094f936a201bfc7c2b5f/librt-0.7.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d39079379a9a28e74f4d57dc6357fa310a1977b51ff12239d7271ec7e71d67f5", size = 174827, upload-time = "2025-12-06T19:03:56.082Z" }, + { url = "https://files.pythonhosted.org/packages/78/21/5b8c60ea208bc83dd00421022a3874330685d7e856404128dc3728d5d1af/librt-0.7.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8837d5a52a2d7aa9f4c3220a8484013aed1d8ad75240d9a75ede63709ef89055", size = 189612, upload-time = "2025-12-06T19:03:57.507Z" }, + { url = "https://files.pythonhosted.org/packages/da/2f/8b819169ef696421fb81cd04c6cdf225f6e96f197366001e9d45180d7e9e/librt-0.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:399bbd7bcc1633c3e356ae274a1deb8781c7bf84d9c7962cc1ae0c6e87837292", size = 184584, upload-time = "2025-12-06T19:03:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fc/af9d225a9395b77bd7678362cb055d0b8139c2018c37665de110ca388022/librt-0.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d8cf653e798ee4c4e654062b633db36984a1572f68c3aa25e364a0ddfbbb910", size = 178269, upload-time = "2025-12-06T19:03:59.769Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d8/7b4fa1683b772966749d5683aa3fd605813defffe157833a8fa69cc89207/librt-0.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f03484b54bf4ae80ab2e504a8d99d20d551bfe64a7ec91e218010b467d77093", size = 199852, upload-time = "2025-12-06T19:04:00.901Z" }, + { url = "https://files.pythonhosted.org/packages/77/e8/4598413aece46ca38d9260ef6c51534bd5f34b5c21474fcf210ce3a02123/librt-0.7.3-cp313-cp313-win32.whl", hash = "sha256:44b3689b040df57f492e02cd4f0bacd1b42c5400e4b8048160c9d5e866de8abe", size = 47936, upload-time = "2025-12-06T19:04:02.054Z" }, + { url = "https://files.pythonhosted.org/packages/af/80/ac0e92d5ef8c6791b3e2c62373863827a279265e0935acdf807901353b0e/librt-0.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6b407c23f16ccc36614c136251d6b32bf30de7a57f8e782378f1107be008ddb0", size = 54965, upload-time = "2025-12-06T19:04:03.224Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fd/042f823fcbff25c1449bb4203a29919891ca74141b68d3a5f6612c4ce283/librt-0.7.3-cp313-cp313-win_arm64.whl", hash = "sha256:abfc57cab3c53c4546aee31859ef06753bfc136c9d208129bad23e2eca39155a", size = 48350, upload-time = "2025-12-06T19:04:04.234Z" }, ] [[package]] @@ -4140,7 +4163,7 @@ wheels = [ [[package]] name = "networkx" -version = "3.6" +version = "3.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin'", @@ -4162,9 +4185,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version == '3.11.*' and platform_python_implementation == 'PyPy' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/fc/7b6fd4d22c8c4dc5704430140d8b3f520531d4fe7328b8f8d03f5a7950e8/networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad", size = 2511464, upload-time = "2025-11-24T03:03:47.158Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c7/d64168da60332c17d24c0d2f08bdf3987e8d1ae9d84b5bbd0eec2eb26a55/networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f", size = 2063713, upload-time = "2025-11-24T03:03:45.21Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] [[package]] @@ -4758,68 +4781,68 @@ wheels = [ [[package]] name = "orjson" -version = "3.11.4" +version = "3.11.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c6/fe/ed708782d6709cc60eb4c2d8a361a440661f74134675c72990f2c48c785f/orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d", size = 5945188, upload-time = "2025-10-24T15:50:38.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347, upload-time = "2025-12-06T15:55:39.458Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/30/5aed63d5af1c8b02fbd2a8d83e2a6c8455e30504c50dbf08c8b51403d873/orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1", size = 243870, upload-time = "2025-10-24T15:48:28.908Z" }, - { url = "https://files.pythonhosted.org/packages/44/1f/da46563c08bef33c41fd63c660abcd2184b4d2b950c8686317d03b9f5f0c/orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44", size = 130622, upload-time = "2025-10-24T15:48:31.361Z" }, - { url = "https://files.pythonhosted.org/packages/02/bd/b551a05d0090eab0bf8008a13a14edc0f3c3e0236aa6f5b697760dd2817b/orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c", size = 129344, upload-time = "2025-10-24T15:48:32.71Z" }, - { url = "https://files.pythonhosted.org/packages/87/6c/9ddd5e609f443b2548c5e7df3c44d0e86df2c68587a0e20c50018cdec535/orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23", size = 136633, upload-time = "2025-10-24T15:48:34.128Z" }, - { url = "https://files.pythonhosted.org/packages/95/f2/9f04f2874c625a9fb60f6918c33542320661255323c272e66f7dcce14df2/orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea", size = 137695, upload-time = "2025-10-24T15:48:35.654Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c2/c7302afcbdfe8a891baae0e2cee091583a30e6fa613e8bdf33b0e9c8a8c7/orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba", size = 136879, upload-time = "2025-10-24T15:48:37.483Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3a/b31c8f0182a3e27f48e703f46e61bb769666cd0dac4700a73912d07a1417/orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff", size = 136374, upload-time = "2025-10-24T15:48:38.624Z" }, - { url = "https://files.pythonhosted.org/packages/29/d0/fd9ab96841b090d281c46df566b7f97bc6c8cd9aff3f3ebe99755895c406/orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac", size = 140519, upload-time = "2025-10-24T15:48:39.756Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ce/36eb0f15978bb88e33a3480e1a3fb891caa0f189ba61ce7713e0ccdadabf/orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79", size = 406522, upload-time = "2025-10-24T15:48:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/85/11/e8af3161a288f5c6a00c188fc729c7ba193b0cbc07309a1a29c004347c30/orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827", size = 149790, upload-time = "2025-10-24T15:48:42.664Z" }, - { url = "https://files.pythonhosted.org/packages/ea/96/209d52db0cf1e10ed48d8c194841e383e23c2ced5a2ee766649fe0e32d02/orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b", size = 140040, upload-time = "2025-10-24T15:48:44.042Z" }, - { url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955, upload-time = "2025-10-24T15:48:45.495Z" }, - { url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577, upload-time = "2025-10-24T15:48:46.609Z" }, - { url = "https://files.pythonhosted.org/packages/63/1d/1ea6005fffb56715fd48f632611e163d1604e8316a5bad2288bee9a1c9eb/orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39", size = 243498, upload-time = "2025-10-24T15:48:48.101Z" }, - { url = "https://files.pythonhosted.org/packages/37/d7/ffed10c7da677f2a9da307d491b9eb1d0125b0307019c4ad3d665fd31f4f/orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d", size = 128961, upload-time = "2025-10-24T15:48:49.571Z" }, - { url = "https://files.pythonhosted.org/packages/a2/96/3e4d10a18866d1368f73c8c44b7fe37cc8a15c32f2a7620be3877d4c55a3/orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175", size = 130321, upload-time = "2025-10-24T15:48:50.713Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1f/465f66e93f434f968dd74d5b623eb62c657bdba2332f5a8be9f118bb74c7/orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040", size = 129207, upload-time = "2025-10-24T15:48:52.193Z" }, - { url = "https://files.pythonhosted.org/packages/28/43/d1e94837543321c119dff277ae8e348562fe8c0fafbb648ef7cb0c67e521/orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63", size = 136323, upload-time = "2025-10-24T15:48:54.806Z" }, - { url = "https://files.pythonhosted.org/packages/bf/04/93303776c8890e422a5847dd012b4853cdd88206b8bbd3edc292c90102d1/orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9", size = 137440, upload-time = "2025-10-24T15:48:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ef/75519d039e5ae6b0f34d0336854d55544ba903e21bf56c83adc51cd8bf82/orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a", size = 136680, upload-time = "2025-10-24T15:48:57.476Z" }, - { url = "https://files.pythonhosted.org/packages/b5/18/bf8581eaae0b941b44efe14fee7b7862c3382fbc9a0842132cfc7cf5ecf4/orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be", size = 136160, upload-time = "2025-10-24T15:48:59.631Z" }, - { url = "https://files.pythonhosted.org/packages/c4/35/a6d582766d351f87fc0a22ad740a641b0a8e6fc47515e8614d2e4790ae10/orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7", size = 140318, upload-time = "2025-10-24T15:49:00.834Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/5a4801803ab2e2e2d703bce1a56540d9f99a9143fbec7bf63d225044fef8/orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549", size = 406330, upload-time = "2025-10-24T15:49:02.327Z" }, - { url = "https://files.pythonhosted.org/packages/80/55/a8f682f64833e3a649f620eafefee175cbfeb9854fc5b710b90c3bca45df/orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905", size = 149580, upload-time = "2025-10-24T15:49:03.517Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e4/c132fa0c67afbb3eb88274fa98df9ac1f631a675e7877037c611805a4413/orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907", size = 139846, upload-time = "2025-10-24T15:49:04.761Z" }, - { url = "https://files.pythonhosted.org/packages/54/06/dc3491489efd651fef99c5908e13951abd1aead1257c67f16135f95ce209/orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c", size = 135781, upload-time = "2025-10-24T15:49:05.969Z" }, - { url = "https://files.pythonhosted.org/packages/79/b7/5e5e8d77bd4ea02a6ac54c42c818afb01dd31961be8a574eb79f1d2cfb1e/orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a", size = 131391, upload-time = "2025-10-24T15:49:07.355Z" }, - { url = "https://files.pythonhosted.org/packages/0f/dc/9484127cc1aa213be398ed735f5f270eedcb0c0977303a6f6ddc46b60204/orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045", size = 126252, upload-time = "2025-10-24T15:49:08.869Z" }, - { url = "https://files.pythonhosted.org/packages/63/51/6b556192a04595b93e277a9ff71cd0cc06c21a7df98bcce5963fa0f5e36f/orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50", size = 243571, upload-time = "2025-10-24T15:49:10.008Z" }, - { url = "https://files.pythonhosted.org/packages/1c/2c/2602392ddf2601d538ff11848b98621cd465d1a1ceb9db9e8043181f2f7b/orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853", size = 128891, upload-time = "2025-10-24T15:49:11.297Z" }, - { url = "https://files.pythonhosted.org/packages/4e/47/bf85dcf95f7a3a12bf223394a4f849430acd82633848d52def09fa3f46ad/orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938", size = 130137, upload-time = "2025-10-24T15:49:12.544Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/a0cb31007f3ab6f1fd2a1b17057c7c349bc2baf8921a85c0180cc7be8011/orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415", size = 129152, upload-time = "2025-10-24T15:49:13.754Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ef/2811def7ce3d8576b19e3929fff8f8f0d44bc5eb2e0fdecb2e6e6cc6c720/orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44", size = 136834, upload-time = "2025-10-24T15:49:15.307Z" }, - { url = "https://files.pythonhosted.org/packages/00/d4/9aee9e54f1809cec8ed5abd9bc31e8a9631d19460e3b8470145d25140106/orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2", size = 137519, upload-time = "2025-10-24T15:49:16.557Z" }, - { url = "https://files.pythonhosted.org/packages/db/ea/67bfdb5465d5679e8ae8d68c11753aaf4f47e3e7264bad66dc2f2249e643/orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708", size = 136749, upload-time = "2025-10-24T15:49:17.796Z" }, - { url = "https://files.pythonhosted.org/packages/01/7e/62517dddcfce6d53a39543cd74d0dccfcbdf53967017c58af68822100272/orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210", size = 136325, upload-time = "2025-10-24T15:49:19.347Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/40516739f99ab4c7ec3aaa5cc242d341fcb03a45d89edeeaabc5f69cb2cf/orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241", size = 140204, upload-time = "2025-10-24T15:49:20.545Z" }, - { url = "https://files.pythonhosted.org/packages/82/18/ff5734365623a8916e3a4037fcef1cd1782bfc14cf0992afe7940c5320bf/orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b", size = 406242, upload-time = "2025-10-24T15:49:21.884Z" }, - { url = "https://files.pythonhosted.org/packages/e1/43/96436041f0a0c8c8deca6a05ebeaf529bf1de04839f93ac5e7c479807aec/orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c", size = 150013, upload-time = "2025-10-24T15:49:23.185Z" }, - { url = "https://files.pythonhosted.org/packages/1b/48/78302d98423ed8780479a1e682b9aecb869e8404545d999d34fa486e573e/orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9", size = 139951, upload-time = "2025-10-24T15:49:24.428Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7b/ad613fdcdaa812f075ec0875143c3d37f8654457d2af17703905425981bf/orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa", size = 136049, upload-time = "2025-10-24T15:49:25.973Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3c/9cf47c3ff5f39b8350fb21ba65d789b6a1129d4cbb3033ba36c8a9023520/orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140", size = 131461, upload-time = "2025-10-24T15:49:27.259Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3b/e2425f61e5825dc5b08c2a5a2b3af387eaaca22a12b9c8c01504f8614c36/orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e", size = 126167, upload-time = "2025-10-24T15:49:28.511Z" }, - { url = "https://files.pythonhosted.org/packages/23/15/c52aa7112006b0f3d6180386c3a46ae057f932ab3425bc6f6ac50431cca1/orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534", size = 243525, upload-time = "2025-10-24T15:49:29.737Z" }, - { url = "https://files.pythonhosted.org/packages/ec/38/05340734c33b933fd114f161f25a04e651b0c7c33ab95e9416ade5cb44b8/orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff", size = 128871, upload-time = "2025-10-24T15:49:31.109Z" }, - { url = "https://files.pythonhosted.org/packages/55/b9/ae8d34899ff0c012039b5a7cb96a389b2476e917733294e498586b45472d/orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad", size = 130055, upload-time = "2025-10-24T15:49:33.382Z" }, - { url = "https://files.pythonhosted.org/packages/33/aa/6346dd5073730451bee3681d901e3c337e7ec17342fb79659ec9794fc023/orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5", size = 129061, upload-time = "2025-10-24T15:49:34.935Z" }, - { url = "https://files.pythonhosted.org/packages/39/e4/8eea51598f66a6c853c380979912d17ec510e8e66b280d968602e680b942/orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a", size = 136541, upload-time = "2025-10-24T15:49:36.923Z" }, - { url = "https://files.pythonhosted.org/packages/9a/47/cb8c654fa9adcc60e99580e17c32b9e633290e6239a99efa6b885aba9dbc/orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436", size = 137535, upload-time = "2025-10-24T15:49:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/43/92/04b8cc5c2b729f3437ee013ce14a60ab3d3001465d95c184758f19362f23/orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9", size = 136703, upload-time = "2025-10-24T15:49:40.795Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fd/d0733fcb9086b8be4ebcfcda2d0312865d17d0d9884378b7cffb29d0763f/orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73", size = 136293, upload-time = "2025-10-24T15:49:42.347Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/3c5514e806837c210492d72ae30ccf050ce3f940f45bf085bab272699ef4/orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0", size = 140131, upload-time = "2025-10-24T15:49:43.638Z" }, - { url = "https://files.pythonhosted.org/packages/9c/dd/ba9d32a53207babf65bd510ac4d0faaa818bd0df9a9c6f472fe7c254f2e3/orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196", size = 406164, upload-time = "2025-10-24T15:49:45.498Z" }, - { url = "https://files.pythonhosted.org/packages/8e/f9/f68ad68f4af7c7bde57cd514eaa2c785e500477a8bc8f834838eb696a685/orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a", size = 149859, upload-time = "2025-10-24T15:49:46.981Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d2/7f847761d0c26818395b3d6b21fb6bc2305d94612a35b0a30eae65a22728/orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6", size = 139926, upload-time = "2025-10-24T15:49:48.321Z" }, - { url = "https://files.pythonhosted.org/packages/9f/37/acd14b12dc62db9a0e1d12386271b8661faae270b22492580d5258808975/orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839", size = 136007, upload-time = "2025-10-24T15:49:49.938Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a9/967be009ddf0a1fffd7a67de9c36656b28c763659ef91352acc02cbe364c/orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a", size = 131314, upload-time = "2025-10-24T15:49:51.248Z" }, - { url = "https://files.pythonhosted.org/packages/cb/db/399abd6950fbd94ce125cb8cd1a968def95174792e127b0642781e040ed4/orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de", size = 126152, upload-time = "2025-10-24T15:49:52.922Z" }, + { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719, upload-time = "2025-12-06T15:53:43.877Z" }, + { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467, upload-time = "2025-12-06T15:53:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702, upload-time = "2025-12-06T15:53:46.659Z" }, + { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907, upload-time = "2025-12-06T15:53:48.487Z" }, + { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935, upload-time = "2025-12-06T15:53:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541, upload-time = "2025-12-06T15:53:51.226Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031, upload-time = "2025-12-06T15:53:52.309Z" }, + { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622, upload-time = "2025-12-06T15:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800, upload-time = "2025-12-06T15:53:54.866Z" }, + { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198, upload-time = "2025-12-06T15:53:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984, upload-time = "2025-12-06T15:53:57.746Z" }, + { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272, upload-time = "2025-12-06T15:53:59.769Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360, upload-time = "2025-12-06T15:54:01.25Z" }, + { url = "https://files.pythonhosted.org/packages/fd/68/6b3659daec3a81aed5ab47700adb1a577c76a5452d35b91c88efee89987f/orjson-3.11.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9c8494625ad60a923af6b2b0bd74107146efe9b55099e20d7740d995f338fcd8", size = 245318, upload-time = "2025-12-06T15:54:02.355Z" }, + { url = "https://files.pythonhosted.org/packages/e9/00/92db122261425f61803ccf0830699ea5567439d966cbc35856fe711bfe6b/orjson-3.11.5-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7bb2ce0b82bc9fd1168a513ddae7a857994b780b2945a8c51db4ab1c4b751ebc", size = 129491, upload-time = "2025-12-06T15:54:03.877Z" }, + { url = "https://files.pythonhosted.org/packages/94/4f/ffdcb18356518809d944e1e1f77589845c278a1ebbb5a8297dfefcc4b4cb/orjson-3.11.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67394d3becd50b954c4ecd24ac90b5051ee7c903d167459f93e77fc6f5b4c968", size = 132167, upload-time = "2025-12-06T15:54:04.944Z" }, + { url = "https://files.pythonhosted.org/packages/97/c6/0a8caff96f4503f4f7dd44e40e90f4d14acf80d3b7a97cb88747bb712d3e/orjson-3.11.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:298d2451f375e5f17b897794bcc3e7b821c0f32b4788b9bcae47ada24d7f3cf7", size = 130516, upload-time = "2025-12-06T15:54:06.274Z" }, + { url = "https://files.pythonhosted.org/packages/4d/63/43d4dc9bd9954bff7052f700fdb501067f6fb134a003ddcea2a0bb3854ed/orjson-3.11.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa5e4244063db8e1d87e0f54c3f7522f14b2dc937e65d5241ef0076a096409fd", size = 135695, upload-time = "2025-12-06T15:54:07.702Z" }, + { url = "https://files.pythonhosted.org/packages/87/6f/27e2e76d110919cb7fcb72b26166ee676480a701bcf8fc53ac5d0edce32f/orjson-3.11.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db2088b490761976c1b2e956d5d4e6409f3732e9d79cfa69f876c5248d1baf9", size = 139664, upload-time = "2025-12-06T15:54:08.828Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/5966153a5f1be49b5fbb8ca619a529fde7bc71aa0a376f2bb83fed248bcd/orjson-3.11.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2ed66358f32c24e10ceea518e16eb3549e34f33a9d51f99ce23b0251776a1ef", size = 137289, upload-time = "2025-12-06T15:54:09.898Z" }, + { url = "https://files.pythonhosted.org/packages/a7/34/8acb12ff0299385c8bbcbb19fbe40030f23f15a6de57a9c587ebf71483fb/orjson-3.11.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2021afda46c1ed64d74b555065dbd4c2558d510d8cec5ea6a53001b3e5e82a9", size = 138784, upload-time = "2025-12-06T15:54:11.022Z" }, + { url = "https://files.pythonhosted.org/packages/ee/27/910421ea6e34a527f73d8f4ee7bdffa48357ff79c7b8d6eb6f7b82dd1176/orjson-3.11.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b42ffbed9128e547a1647a3e50bc88ab28ae9daa61713962e0d3dd35e820c125", size = 141322, upload-time = "2025-12-06T15:54:12.427Z" }, + { url = "https://files.pythonhosted.org/packages/87/a3/4b703edd1a05555d4bb1753d6ce44e1a05b7a6d7c164d5b332c795c63d70/orjson-3.11.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8d5f16195bb671a5dd3d1dbea758918bada8f6cc27de72bd64adfbd748770814", size = 413612, upload-time = "2025-12-06T15:54:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/1b/36/034177f11d7eeea16d3d2c42a1883b0373978e08bc9dad387f5074c786d8/orjson-3.11.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c0e5d9f7a0227df2927d343a6e3859bebf9208b427c79bd31949abcc2fa32fa5", size = 150993, upload-time = "2025-12-06T15:54:15.189Z" }, + { url = "https://files.pythonhosted.org/packages/44/2f/ea8b24ee046a50a7d141c0227c4496b1180b215e728e3b640684f0ea448d/orjson-3.11.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23d04c4543e78f724c4dfe656b3791b5f98e4c9253e13b2636f1af5d90e4a880", size = 141774, upload-time = "2025-12-06T15:54:16.451Z" }, + { url = "https://files.pythonhosted.org/packages/8a/12/cc440554bf8200eb23348a5744a575a342497b65261cd65ef3b28332510a/orjson-3.11.5-cp311-cp311-win32.whl", hash = "sha256:c404603df4865f8e0afe981aa3c4b62b406e6d06049564d58934860b62b7f91d", size = 135109, upload-time = "2025-12-06T15:54:17.73Z" }, + { url = "https://files.pythonhosted.org/packages/a3/83/e0c5aa06ba73a6760134b169f11fb970caa1525fa4461f94d76e692299d9/orjson-3.11.5-cp311-cp311-win_amd64.whl", hash = "sha256:9645ef655735a74da4990c24ffbd6894828fbfa117bc97c1edd98c282ecb52e1", size = 133193, upload-time = "2025-12-06T15:54:19.426Z" }, + { url = "https://files.pythonhosted.org/packages/cb/35/5b77eaebc60d735e832c5b1a20b155667645d123f09d471db0a78280fb49/orjson-3.11.5-cp311-cp311-win_arm64.whl", hash = "sha256:1cbf2735722623fcdee8e712cbaaab9e372bbcb0c7924ad711b261c2eccf4a5c", size = 126830, upload-time = "2025-12-06T15:54:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a4/8052a029029b096a78955eadd68ab594ce2197e24ec50e6b6d2ab3f4e33b/orjson-3.11.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:334e5b4bff9ad101237c2d799d9fd45737752929753bf4faf4b207335a416b7d", size = 245347, upload-time = "2025-12-06T15:54:22.061Z" }, + { url = "https://files.pythonhosted.org/packages/64/67/574a7732bd9d9d79ac620c8790b4cfe0717a3d5a6eb2b539e6e8995e24a0/orjson-3.11.5-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:ff770589960a86eae279f5d8aa536196ebda8273a2a07db2a54e82b93bc86626", size = 129435, upload-time = "2025-12-06T15:54:23.615Z" }, + { url = "https://files.pythonhosted.org/packages/52/8d/544e77d7a29d90cf4d9eecd0ae801c688e7f3d1adfa2ebae5e1e94d38ab9/orjson-3.11.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed24250e55efbcb0b35bed7caaec8cedf858ab2f9f2201f17b8938c618c8ca6f", size = 132074, upload-time = "2025-12-06T15:54:24.694Z" }, + { url = "https://files.pythonhosted.org/packages/6e/57/b9f5b5b6fbff9c26f77e785baf56ae8460ef74acdb3eae4931c25b8f5ba9/orjson-3.11.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66d7769e98a08a12a139049aac2f0ca3adae989817f8c43337455fbc7669b85", size = 130520, upload-time = "2025-12-06T15:54:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6d/d34970bf9eb33f9ec7c979a262cad86076814859e54eb9a059a52f6dc13d/orjson-3.11.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86cfc555bfd5794d24c6a1903e558b50644e5e68e6471d66502ce5cb5fdef3f9", size = 136209, upload-time = "2025-12-06T15:54:27.264Z" }, + { url = "https://files.pythonhosted.org/packages/e7/39/bc373b63cc0e117a105ea12e57280f83ae52fdee426890d57412432d63b3/orjson-3.11.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a230065027bc2a025e944f9d4714976a81e7ecfa940923283bca7bbc1f10f626", size = 139837, upload-time = "2025-12-06T15:54:28.75Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7c4818c8d7d324da220f4f1af55c343956003aa4d1ce1857bdc1d396ba69/orjson-3.11.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b29d36b60e606df01959c4b982729c8845c69d1963f88686608be9ced96dbfaa", size = 137307, upload-time = "2025-12-06T15:54:29.856Z" }, + { url = "https://files.pythonhosted.org/packages/46/bf/0993b5a056759ba65145effe3a79dd5a939d4a070eaa5da2ee3180fbb13f/orjson-3.11.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74099c6b230d4261fdc3169d50efc09abf38ace1a42ea2f9994b1d79153d477", size = 139020, upload-time = "2025-12-06T15:54:31.024Z" }, + { url = "https://files.pythonhosted.org/packages/65/e8/83a6c95db3039e504eda60fc388f9faedbb4f6472f5aba7084e06552d9aa/orjson-3.11.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e697d06ad57dd0c7a737771d470eedc18e68dfdefcdd3b7de7f33dfda5b6212e", size = 141099, upload-time = "2025-12-06T15:54:32.196Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b4/24fdc024abfce31c2f6812973b0a693688037ece5dc64b7a60c1ce69e2f2/orjson-3.11.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e08ca8a6c851e95aaecc32bc44a5aa75d0ad26af8cdac7c77e4ed93acf3d5b69", size = 413540, upload-time = "2025-12-06T15:54:33.361Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/01c0ec95d55ed0c11e4cae3e10427e479bba40c77312b63e1f9665e0737d/orjson-3.11.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8b5f96c05fce7d0218df3fdfeb962d6b8cfff7e3e20264306b46dd8b217c0f3", size = 151530, upload-time = "2025-12-06T15:54:34.6Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d4/f9ebc57182705bb4bbe63f5bbe14af43722a2533135e1d2fb7affa0c355d/orjson-3.11.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddbfdb5099b3e6ba6d6ea818f61997bb66de14b411357d24c4612cf1ebad08ca", size = 141863, upload-time = "2025-12-06T15:54:35.801Z" }, + { url = "https://files.pythonhosted.org/packages/0d/04/02102b8d19fdcb009d72d622bb5781e8f3fae1646bf3e18c53d1bc8115b5/orjson-3.11.5-cp312-cp312-win32.whl", hash = "sha256:9172578c4eb09dbfcf1657d43198de59b6cef4054de385365060ed50c458ac98", size = 135255, upload-time = "2025-12-06T15:54:37.209Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fb/f05646c43d5450492cb387de5549f6de90a71001682c17882d9f66476af5/orjson-3.11.5-cp312-cp312-win_amd64.whl", hash = "sha256:2b91126e7b470ff2e75746f6f6ee32b9ab67b7a93c8ba1d15d3a0caaf16ec875", size = 133252, upload-time = "2025-12-06T15:54:38.401Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/7b8c0b26ba18c793533ac1cd145e131e46fcf43952aa94c109b5b913c1f0/orjson-3.11.5-cp312-cp312-win_arm64.whl", hash = "sha256:acbc5fac7e06777555b0722b8ad5f574739e99ffe99467ed63da98f97f9ca0fe", size = 126777, upload-time = "2025-12-06T15:54:39.515Z" }, + { url = "https://files.pythonhosted.org/packages/10/43/61a77040ce59f1569edf38f0b9faadc90c8cf7e9bec2e0df51d0132c6bb7/orjson-3.11.5-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3b01799262081a4c47c035dd77c1301d40f568f77cc7ec1bb7db5d63b0a01629", size = 245271, upload-time = "2025-12-06T15:54:40.878Z" }, + { url = "https://files.pythonhosted.org/packages/55/f9/0f79be617388227866d50edd2fd320cb8fb94dc1501184bb1620981a0aba/orjson-3.11.5-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:61de247948108484779f57a9f406e4c84d636fa5a59e411e6352484985e8a7c3", size = 129422, upload-time = "2025-12-06T15:54:42.403Z" }, + { url = "https://files.pythonhosted.org/packages/77/42/f1bf1549b432d4a78bfa95735b79b5dac75b65b5bb815bba86ad406ead0a/orjson-3.11.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:894aea2e63d4f24a7f04a1908307c738d0dce992e9249e744b8f4e8dd9197f39", size = 132060, upload-time = "2025-12-06T15:54:43.531Z" }, + { url = "https://files.pythonhosted.org/packages/25/49/825aa6b929f1a6ed244c78acd7b22c1481fd7e5fda047dc8bf4c1a807eb6/orjson-3.11.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ddc21521598dbe369d83d4d40338e23d4101dad21dae0e79fa20465dbace019f", size = 130391, upload-time = "2025-12-06T15:54:45.059Z" }, + { url = "https://files.pythonhosted.org/packages/42/ec/de55391858b49e16e1aa8f0bbbb7e5997b7345d8e984a2dec3746d13065b/orjson-3.11.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cce16ae2f5fb2c53c3eafdd1706cb7b6530a67cc1c17abe8ec747f5cd7c0c51", size = 135964, upload-time = "2025-12-06T15:54:46.576Z" }, + { url = "https://files.pythonhosted.org/packages/1c/40/820bc63121d2d28818556a2d0a09384a9f0262407cf9fa305e091a8048df/orjson-3.11.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e46c762d9f0e1cfb4ccc8515de7f349abbc95b59cb5a2bd68df5973fdef913f8", size = 139817, upload-time = "2025-12-06T15:54:48.084Z" }, + { url = "https://files.pythonhosted.org/packages/09/c7/3a445ca9a84a0d59d26365fd8898ff52bdfcdcb825bcc6519830371d2364/orjson-3.11.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7345c759276b798ccd6d77a87136029e71e66a8bbf2d2755cbdde1d82e78706", size = 137336, upload-time = "2025-12-06T15:54:49.426Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b3/dc0d3771f2e5d1f13368f56b339c6782f955c6a20b50465a91acb79fe961/orjson-3.11.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75bc2e59e6a2ac1dd28901d07115abdebc4563b5b07dd612bf64260a201b1c7f", size = 138993, upload-time = "2025-12-06T15:54:50.939Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a2/65267e959de6abe23444659b6e19c888f242bf7725ff927e2292776f6b89/orjson-3.11.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54aae9b654554c3b4edd61896b978568c6daa16af96fa4681c9b5babd469f863", size = 141070, upload-time = "2025-12-06T15:54:52.414Z" }, + { url = "https://files.pythonhosted.org/packages/63/c9/da44a321b288727a322c6ab17e1754195708786a04f4f9d2220a5076a649/orjson-3.11.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4bdd8d164a871c4ec773f9de0f6fe8769c2d6727879c37a9666ba4183b7f8228", size = 413505, upload-time = "2025-12-06T15:54:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/7f/17/68dc14fa7000eefb3d4d6d7326a190c99bb65e319f02747ef3ebf2452f12/orjson-3.11.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a261fef929bcf98a60713bf5e95ad067cea16ae345d9a35034e73c3990e927d2", size = 151342, upload-time = "2025-12-06T15:54:55.113Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c5/ccee774b67225bed630a57478529fc026eda33d94fe4c0eac8fe58d4aa52/orjson-3.11.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c028a394c766693c5c9909dec76b24f37e6a1b91999e8d0c0d5feecbe93c3e05", size = 141823, upload-time = "2025-12-06T15:54:56.331Z" }, + { url = "https://files.pythonhosted.org/packages/67/80/5d00e4155d0cd7390ae2087130637671da713959bb558db9bac5e6f6b042/orjson-3.11.5-cp313-cp313-win32.whl", hash = "sha256:2cc79aaad1dfabe1bd2d50ee09814a1253164b3da4c00a78c458d82d04b3bdef", size = 135236, upload-time = "2025-12-06T15:54:57.507Z" }, + { url = "https://files.pythonhosted.org/packages/95/fe/792cc06a84808dbdc20ac6eab6811c53091b42f8e51ecebf14b540e9cfe4/orjson-3.11.5-cp313-cp313-win_amd64.whl", hash = "sha256:ff7877d376add4e16b274e35a3f58b7f37b362abf4aa31863dadacdd20e3a583", size = 133167, upload-time = "2025-12-06T15:54:58.71Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/d158bd8b50e3b1cfdcf406a7e463f6ffe3f0d167b99634717acdaf5e299f/orjson-3.11.5-cp313-cp313-win_arm64.whl", hash = "sha256:59ac72ea775c88b163ba8d21b0177628bd015c5dd060647bbab6e22da3aad287", size = 126712, upload-time = "2025-12-06T15:54:59.892Z" }, ] [[package]] @@ -5174,30 +5197,30 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.5.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] [[package]] name = "playwright" -version = "1.56.0" +version = "1.57.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet" }, { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, - { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, - { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, - { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, - { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b6/e17543cea8290ae4dced10be21d5a43c360096aa2cce0aa7039e60c50df3/playwright-1.57.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:9351c1ac3dfd9b3820fe7fc4340d96c0d3736bb68097b9b7a69bd45d25e9370c", size = 41985039, upload-time = "2025-12-09T08:06:18.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/04/ef95b67e1ff59c080b2effd1a9a96984d6953f667c91dfe9d77c838fc956/playwright-1.57.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4a9d65027bce48eeba842408bcc1421502dfd7e41e28d207e94260fa93ca67e", size = 40775575, upload-time = "2025-12-09T08:06:22.105Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/5563850322a663956c927eefcf1457d12917e8f118c214410e815f2147d1/playwright-1.57.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:99104771abc4eafee48f47dac2369e0015516dc1ce8c409807d2dd440828b9a4", size = 41985042, upload-time = "2025-12-09T08:06:25.357Z" }, + { url = "https://files.pythonhosted.org/packages/56/61/3a803cb5ae0321715bfd5247ea871d25b32c8f372aeb70550a90c5f586df/playwright-1.57.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:284ed5a706b7c389a06caa431b2f0ba9ac4130113c3a779767dda758c2497bb1", size = 45975252, upload-time = "2025-12-09T08:06:29.186Z" }, + { url = "https://files.pythonhosted.org/packages/83/d7/b72eb59dfbea0013a7f9731878df8c670f5f35318cedb010c8a30292c118/playwright-1.57.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a1bae6c0a07839cdeaddbc0756b3b2b85e476c07945f64ece08f1f956a86f1", size = 45706917, upload-time = "2025-12-09T08:06:32.549Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/3fc9ebd7c95ee54ba6a68d5c0bc23e449f7235f4603fc60534a364934c16/playwright-1.57.0-py3-none-win32.whl", hash = "sha256:1dd93b265688da46e91ecb0606d36f777f8eadcf7fbef12f6426b20bf0c9137c", size = 36553860, upload-time = "2025-12-09T08:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/58/d4/dcdfd2a33096aeda6ca0d15584800443dd2be64becca8f315634044b135b/playwright-1.57.0-py3-none-win_amd64.whl", hash = "sha256:6caefb08ed2c6f29d33b8088d05d09376946e49a73be19271c8cd5384b82b14c", size = 36553864, upload-time = "2025-12-09T08:06:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/6a/60/fe31d7e6b8907789dcb0584f88be741ba388413e4fbce35f1eba4e3073de/playwright-1.57.0-py3-none-win_arm64.whl", hash = "sha256:5f065f5a133dbc15e6e7c71e7bc04f258195755b1c32a432b792e28338c8335e", size = 32837940, upload-time = "2025-12-09T08:06:42.268Z" }, ] [[package]] @@ -5575,127 +5598,132 @@ wheels = [ [[package]] name = "pybase64" -version = "1.4.2" +version = "1.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/14/43297a7b7f0c1bf0c00b596f754ee3ac946128c64d21047ccf9c9bbc5165/pybase64-1.4.2.tar.gz", hash = "sha256:46cdefd283ed9643315d952fe44de80dc9b9a811ce6e3ec97fd1827af97692d0", size = 137246, upload-time = "2025-07-27T13:08:57.808Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6d/0a7159c24ed35c8b9190b148376ad9b96598354f94ede29df74861da9ec6/pybase64-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82b4593b480773b17698fef33c68bae0e1c474ba07663fad74249370c46b46c9", size = 38240, upload-time = "2025-07-27T13:02:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/86/2e/dad4cd832a90a49d98867e824180585e7c928504987d37304bccae11a314/pybase64-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a126f29d29cb4a498db179135dbf955442a0de5b00f374523f5dcceb9074ff58", size = 31658, upload-time = "2025-07-27T13:02:20.823Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d8/30ea35dc2c8c568be93e1379efcaa35092e37efa2ce7f1985ccc63babee7/pybase64-1.4.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1eef93c29cc5567480d168f9cc1ebd3fc3107c65787aed2019a8ea68575a33e0", size = 65963, upload-time = "2025-07-27T13:02:22.376Z" }, - { url = "https://files.pythonhosted.org/packages/f6/da/1c22f2a21d6bb9ec2a214d15ae02d5b20a95335de218a0ecbf769c535a5c/pybase64-1.4.2-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:17b871a34aaeb0644145cb6bf28feb163f593abea11aec3dbcc34a006edfc828", size = 68887, upload-time = "2025-07-27T13:02:23.606Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8d/e04d489ba99b444ce94b4d5b232365d00b0f0e8564275d7ba7434dcabe72/pybase64-1.4.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1f734e16293637a35d282ce594eb05a7a90ea3ae2bc84a3496a5df9e6b890725", size = 57503, upload-time = "2025-07-27T13:02:24.83Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b8/5ec9c334f30cf898709a084d596bf4b47aec2e07870f07bac5cf39754eca/pybase64-1.4.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:22bd38db2d990d5545dde83511edeec366630d00679dbd945472315c09041dc6", size = 54517, upload-time = "2025-07-27T13:02:26.006Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5a/6e4424ecca041e53aa7c14525f99edd43d0117c23c5d9cb14e931458a536/pybase64-1.4.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:dc65cee686dda72007b7541b2014f33ee282459c781b9b61305bd8b9cfadc8e1", size = 57167, upload-time = "2025-07-27T13:02:27.47Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d0/13f1a9467cf565eecc21dce89fb0723458d8c563d2ccfb99b96e8318dfd5/pybase64-1.4.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1e79641c420a22e49c67c046895efad05bf5f8b1dbe0dd78b4af3ab3f2923fe2", size = 57718, upload-time = "2025-07-27T13:02:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/d80335c36ad9400b18b4f92e9f680cf7646102fe4919f7bce5786a2ccb7b/pybase64-1.4.2-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:12f5e7db522ef780a8b333dab5f7d750d270b23a1684bc2235ba50756c7ba428", size = 53021, upload-time = "2025-07-27T13:02:29.823Z" }, - { url = "https://files.pythonhosted.org/packages/68/57/504ff75f7c78df28be126fe6634083d28d7f84c17e04a74a7dcb50ab2377/pybase64-1.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a618b1e1a63e75dd40c2a397d875935ed0835464dc55cb1b91e8f880113d0444", size = 56306, upload-time = "2025-07-27T13:02:31.314Z" }, - { url = "https://files.pythonhosted.org/packages/bf/bc/2d21cda8b73c8c9f5cd3d7e6e26dd6dfc96491052112f282332a3d5bf1d9/pybase64-1.4.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89b0a51702c7746fa914e75e680ad697b979cdead6b418603f56a6fc9de2f50f", size = 50101, upload-time = "2025-07-27T13:02:32.662Z" }, - { url = "https://files.pythonhosted.org/packages/88/6d/51942e7737bb0711ca3e55db53924fd7f07166d79da5508ab8f5fd5972a8/pybase64-1.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5161b8b82f8ba5dbbc3f76e0270622a2c2fdb9ffaf092d8f774ad7ec468c027", size = 66555, upload-time = "2025-07-27T13:02:34.122Z" }, - { url = "https://files.pythonhosted.org/packages/b6/c8/c46024d196402e7be4d3fad85336863a34816c3436c51fcf9c7c0781bf11/pybase64-1.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2168de920c9b1e57850e9ff681852923a953601f73cc96a0742a42236695c316", size = 55684, upload-time = "2025-07-27T13:02:35.427Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c5/953782c9d599ff5217ee87f19e317c494cd4840afcab4c48f99cb78ca201/pybase64-1.4.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7a1e3dc977562abe40ab43483223013be71b215a5d5f3c78a666e70a5076eeec", size = 52475, upload-time = "2025-07-27T13:02:36.634Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/57d36173631aab67ca4558cdbde1047fc67a09b77f9c53addd57c7e9fdd4/pybase64-1.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4cf1e8a57449e48137ef4de00a005e24c3f1cffc0aafc488e36ceb5bb2cbb1da", size = 53943, upload-time = "2025-07-27T13:02:37.777Z" }, - { url = "https://files.pythonhosted.org/packages/75/73/23e5bb0bffac0cabe2d11d1c618f6ef73da9f430da03c5249931e3c49b63/pybase64-1.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8e1a381ba124f26a93d5925efbf6e6c36287fc2c93d74958e8b677c30a53fc0", size = 68411, upload-time = "2025-07-27T13:02:39.302Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e7/0d5c99e5e61ff5e46949a0128b49fc2c47afc0d2b815333459b17aa9d467/pybase64-1.4.2-cp310-cp310-win32.whl", hash = "sha256:8fdd9c5b60ec9a1db854f5f96bba46b80a9520069282dc1d37ff433eb8248b1f", size = 33614, upload-time = "2025-07-27T13:02:40.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/40/879b6de61d7c07a2cbf76b75e9739c4938c3a1f66ac03243f2ff7ec9fb6b/pybase64-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:37a6c73f14c6539c0ad1aebf0cce92138af25c99a6e7aee637d9f9fc634c8a40", size = 35790, upload-time = "2025-07-27T13:02:41.864Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e2/75cec12880ce3f47a79a2b9a0cdc766dc0429a7ce967bb3ab3a4b55a7f6b/pybase64-1.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:b3280d03b7b361622c469d005cc270d763d9e29d0a490c26addb4f82dfe71a79", size = 30900, upload-time = "2025-07-27T13:02:43.022Z" }, - { url = "https://files.pythonhosted.org/packages/da/fb/edaa56bbf04715efc3c36966cc0150e01d7a8336c3da182f850b7fd43d32/pybase64-1.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26284ef64f142067293347bcc9d501d2b5d44b92eab9d941cb10a085fb01c666", size = 38238, upload-time = "2025-07-27T13:02:44.224Z" }, - { url = "https://files.pythonhosted.org/packages/28/a4/ca1538e9adf08f5016b3543b0060c18aea9a6e805dd20712a197c509d90d/pybase64-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52dd32fe5cbfd8af8f3f034a4a65ee61948c72e5c358bf69d59543fc0dbcf950", size = 31659, upload-time = "2025-07-27T13:02:45.445Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8f/f9b49926a60848ba98350dd648227ec524fb78340b47a450c4dbaf24b1bb/pybase64-1.4.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:37f133e8c96427995480bb6d396d9d49e949a3e829591845bb6a5a7f215ca177", size = 68318, upload-time = "2025-07-27T13:02:46.644Z" }, - { url = "https://files.pythonhosted.org/packages/29/9b/6ed2dd2bc8007f33b8316d6366b0901acbdd5665b419c2893b3dd48708de/pybase64-1.4.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ee3874b0abbdd4c903d3989682a3f016fd84188622879f6f95a5dc5718d7e5", size = 71357, upload-time = "2025-07-27T13:02:47.937Z" }, - { url = "https://files.pythonhosted.org/packages/fb/69/be9ac8127da8d8339db7129683bd2975cecb0bf40a82731e1a492577a177/pybase64-1.4.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c69f177b1e404b22b05802127d6979acf4cb57f953c7de9472410f9c3fdece7", size = 59817, upload-time = "2025-07-27T13:02:49.163Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a2/e3e09e000b509609276ee28b71beb0b61462d4a43b3e0db0a44c8652880c/pybase64-1.4.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:80c817e88ef2ca3cc9a285fde267690a1cb821ce0da4848c921c16f0fec56fda", size = 56639, upload-time = "2025-07-27T13:02:50.384Z" }, - { url = "https://files.pythonhosted.org/packages/01/70/ad7eff88aa4f1be06db705812e1f01749606933bf8fe9df553bb04b703e6/pybase64-1.4.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a4bb6e7e45bfdaea0f2aaf022fc9a013abe6e46ccea31914a77e10f44098688", size = 59368, upload-time = "2025-07-27T13:02:51.883Z" }, - { url = "https://files.pythonhosted.org/packages/9d/82/0cd1b4bcd2a4da7805cfa04587be783bf9583b34ac16cadc29cf119a4fa2/pybase64-1.4.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2710a80d41a2b41293cb0e5b84b5464f54aa3f28f7c43de88784d2d9702b8a1c", size = 59981, upload-time = "2025-07-27T13:02:53.16Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4c/8029a03468307dfaf0f9694d31830487ee43af5f8a73407004907724e8ac/pybase64-1.4.2-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:aa6122c8a81f6597e1c1116511f03ed42cf377c2100fe7debaae7ca62521095a", size = 54908, upload-time = "2025-07-27T13:02:54.363Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8b/70bd0fe659e242efd0f60895a8ce1fe88e3a4084fd1be368974c561138c9/pybase64-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7e22b02505d64db308e9feeb6cb52f1d554ede5983de0befa59ac2d2ffb6a5f", size = 58650, upload-time = "2025-07-27T13:02:55.905Z" }, - { url = "https://files.pythonhosted.org/packages/64/ca/9c1d23cbc4b9beac43386a32ad53903c816063cef3f14c10d7c3d6d49a23/pybase64-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:edfe4a3c8c4007f09591f49b46a89d287ef5e8cd6630339536fe98ff077263c2", size = 52323, upload-time = "2025-07-27T13:02:57.192Z" }, - { url = "https://files.pythonhosted.org/packages/aa/29/a6292e9047248c8616dc53131a49da6c97a61616f80e1e36c73d7ef895fe/pybase64-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b79b4a53dd117ffbd03e96953f2e6bd2827bfe11afeb717ea16d9b0893603077", size = 68979, upload-time = "2025-07-27T13:02:58.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e0/cfec7b948e170395d8e88066e01f50e71195db9837151db10c14965d6222/pybase64-1.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fd9afa7a61d89d170607faf22287290045757e782089f0357b8f801d228d52c3", size = 58037, upload-time = "2025-07-27T13:02:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/74/7e/0ac1850198c9c35ef631174009cee576f4d8afff3bf493ce310582976ab4/pybase64-1.4.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5c17b092e4da677a595178d2db17a5d2fafe5c8e418d46c0c4e4cde5adb8cff3", size = 54416, upload-time = "2025-07-27T13:03:00.978Z" }, - { url = "https://files.pythonhosted.org/packages/1b/45/b0b037f27e86c50e62d927f0bc1bde8b798dd55ab39197b116702e508d05/pybase64-1.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:120799274cf55f3f5bb8489eaa85142f26170564baafa7cf3e85541c46b6ab13", size = 56257, upload-time = "2025-07-27T13:03:02.201Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0d/5034598aac56336d88fd5aaf6f34630330643b51d399336b8c788d798fc5/pybase64-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:522e4e712686acec2d25de9759dda0b0618cb9f6588523528bc74715c0245c7b", size = 70889, upload-time = "2025-07-27T13:03:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3b/0645f21bb08ecf45635b624958b5f9e569069d31ecbf125dc7e0e5b83f60/pybase64-1.4.2-cp311-cp311-win32.whl", hash = "sha256:bfd828792982db8d787515535948c1e340f1819407c8832f94384c0ebeaf9d74", size = 33631, upload-time = "2025-07-27T13:03:05.194Z" }, - { url = "https://files.pythonhosted.org/packages/8f/08/24f8103c1f19e78761026cdd9f3b3be73239bc19cf5ab6fef0e8042d0bc6/pybase64-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7a9e89d40dbf833af481d1d5f1a44d173c9c4b56a7c8dba98e39a78ee87cfc52", size = 35781, upload-time = "2025-07-27T13:03:06.779Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/832fb035a0ea7eb53d776a5cfa961849e22828f6dfdfcdb9eb43ba3c0166/pybase64-1.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:ce5809fa90619b03eab1cd63fec142e6cf1d361731a9b9feacf27df76c833343", size = 30903, upload-time = "2025-07-27T13:03:07.903Z" }, - { url = "https://files.pythonhosted.org/packages/28/6d/11ede991e800797b9f5ebd528013b34eee5652df93de61ffb24503393fa5/pybase64-1.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2c75d1388855b5a1015b65096d7dbcc708e7de3245dcbedeb872ec05a09326", size = 38326, upload-time = "2025-07-27T13:03:09.065Z" }, - { url = "https://files.pythonhosted.org/packages/fe/84/87f1f565f42e2397e2aaa2477c86419f5173c3699881c42325c090982f0a/pybase64-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b621a972a01841368fdb9dedc55fd3c6e0c7217d0505ba3b1ebe95e7ef1b493", size = 31661, upload-time = "2025-07-27T13:03:10.295Z" }, - { url = "https://files.pythonhosted.org/packages/cb/2a/a24c810e7a61d2cc6f73fe9ee4872a03030887fa8654150901b15f376f65/pybase64-1.4.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f48c32ac6a16cbf57a5a96a073fef6ff7e3526f623cd49faa112b7f9980bafba", size = 68192, upload-time = "2025-07-27T13:03:11.467Z" }, - { url = "https://files.pythonhosted.org/packages/ee/87/d9baf98cbfc37b8657290ad4421f3a3c36aa0eafe4872c5859cfb52f3448/pybase64-1.4.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ace8b23093a6bb862477080d9059b784096ab2f97541e8bfc40d42f062875149", size = 71587, upload-time = "2025-07-27T13:03:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/0b/89/3df043cc56ef3b91b7aa0c26ae822a2d7ec8da0b0fd7c309c879b0eb5988/pybase64-1.4.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1772c7532a7fb6301baea3dd3e010148dbf70cd1136a83c2f5f91bdc94822145", size = 59910, upload-time = "2025-07-27T13:03:14.266Z" }, - { url = "https://files.pythonhosted.org/packages/75/4f/6641e9edf37aeb4d4524dc7ba2168eff8d96c90e77f6283c2be3400ab380/pybase64-1.4.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:f86f7faddcba5cbfea475f8ab96567834c28bf09ca6c7c3d66ee445adac80d8f", size = 56701, upload-time = "2025-07-27T13:03:15.6Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7f/20d8ac1046f12420a0954a45a13033e75f98aade36eecd00c64e3549b071/pybase64-1.4.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b8c8e275b5294089f314814b4a50174ab90af79d6a4850f6ae11261ff6a7372", size = 59288, upload-time = "2025-07-27T13:03:16.823Z" }, - { url = "https://files.pythonhosted.org/packages/17/ea/9c0ca570e3e50b3c6c3442e280c83b321a0464c86a9db1f982a4ff531550/pybase64-1.4.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:864d85a0470c615807ae8b97d724d068b940a2d10ac13a5f1b9e75a3ce441758", size = 60267, upload-time = "2025-07-27T13:03:18.132Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46894929d71ccedebbfb0284173b0fea96bc029cd262654ba8451a7035d6/pybase64-1.4.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:47254d97ed2d8351e30ecfdb9e2414547f66ba73f8a09f932c9378ff75cd10c5", size = 54801, upload-time = "2025-07-27T13:03:19.669Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/02c95218ea964f0b2469717c2c69b48e63f4ca9f18af01a5b2a29e4c1216/pybase64-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:264b65ecc4f0ee73f3298ab83bbd8008f7f9578361b8df5b448f985d8c63e02a", size = 58599, upload-time = "2025-07-27T13:03:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/15/45/ccc21004930789b8fb439d43e3212a6c260ccddb2bf450c39a20db093f33/pybase64-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbcc2b30cd740c16c9699f596f22c7a9e643591311ae72b1e776f2d539e9dd9d", size = 52388, upload-time = "2025-07-27T13:03:23.064Z" }, - { url = "https://files.pythonhosted.org/packages/c4/45/22e46e549710c4c237d77785b6fb1bc4c44c288a5c44237ba9daf5c34b82/pybase64-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cda9f79c22d51ee4508f5a43b673565f1d26af4330c99f114e37e3186fdd3607", size = 68802, upload-time = "2025-07-27T13:03:24.673Z" }, - { url = "https://files.pythonhosted.org/packages/55/0c/232c6261b81296e5593549b36e6e7884a5da008776d12665923446322c36/pybase64-1.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0c91c6d2a7232e2a1cd10b3b75a8bb657defacd4295a1e5e80455df2dfc84d4f", size = 57841, upload-time = "2025-07-27T13:03:25.948Z" }, - { url = "https://files.pythonhosted.org/packages/20/8a/b35a615ae6f04550d696bb179c414538b3b477999435fdd4ad75b76139e4/pybase64-1.4.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a370dea7b1cee2a36a4d5445d4e09cc243816c5bc8def61f602db5a6f5438e52", size = 54320, upload-time = "2025-07-27T13:03:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a9/8bd4f9bcc53689f1b457ecefed1eaa080e4949d65a62c31a38b7253d5226/pybase64-1.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9aa4de83f02e462a6f4e066811c71d6af31b52d7484de635582d0e3ec3d6cc3e", size = 56482, upload-time = "2025-07-27T13:03:28.942Z" }, - { url = "https://files.pythonhosted.org/packages/75/e5/4a7735b54a1191f61c3f5c2952212c85c2d6b06eb5fb3671c7603395f70c/pybase64-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83a1c2f9ed00fee8f064d548c8654a480741131f280e5750bb32475b7ec8ee38", size = 70959, upload-time = "2025-07-27T13:03:30.171Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/e2b6cb32c782e12304d467418e70da0212567f42bd4d3b5eb1fdf64920ad/pybase64-1.4.2-cp312-cp312-win32.whl", hash = "sha256:a6e5688b18d558e8c6b8701cc8560836c4bbeba61d33c836b4dba56b19423716", size = 33683, upload-time = "2025-07-27T13:03:31.775Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bc/d5c277496063a09707486180f17abbdbdebbf2f5c4441b20b11d3cb7dc7c/pybase64-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:c995d21b8bd08aa179cd7dd4db0695c185486ecc72da1e8f6c37ec86cadb8182", size = 35817, upload-time = "2025-07-27T13:03:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/e6/69/e4be18ae685acff0ae77f75d4586590f29d2cd187bf603290cf1d635cad4/pybase64-1.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:e254b9258c40509c2ea063a7784f6994988f3f26099d6e08704e3c15dfed9a55", size = 30900, upload-time = "2025-07-27T13:03:34.499Z" }, - { url = "https://files.pythonhosted.org/packages/f4/56/5337f27a8b8d2d6693f46f7b36bae47895e5820bfa259b0072574a4e1057/pybase64-1.4.2-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:0f331aa59549de21f690b6ccc79360ffed1155c3cfbc852eb5c097c0b8565a2b", size = 33888, upload-time = "2025-07-27T13:03:35.698Z" }, - { url = "https://files.pythonhosted.org/packages/4c/09/f3f4b11fc9beda7e8625e29fb0f549958fcbb34fea3914e1c1d95116e344/pybase64-1.4.2-cp313-cp313-android_21_x86_64.whl", hash = "sha256:9dad20bf1f3ed9e6fe566c4c9d07d9a6c04f5a280daebd2082ffb8620b0a880d", size = 40796, upload-time = "2025-07-27T13:03:36.927Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/470768f0fe6de0aa302a8cb1bdf2f9f5cffc3f69e60466153be68bc953aa/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:69d3f0445b0faeef7bb7f93bf8c18d850785e2a77f12835f49e524cc54af04e7", size = 30914, upload-time = "2025-07-27T13:03:38.475Z" }, - { url = "https://files.pythonhosted.org/packages/75/6b/d328736662665e0892409dc410353ebef175b1be5eb6bab1dad579efa6df/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2372b257b1f4dd512f317fb27e77d313afd137334de64c87de8374027aacd88a", size = 31380, upload-time = "2025-07-27T13:03:39.7Z" }, - { url = "https://files.pythonhosted.org/packages/ca/96/7ff718f87c67f4147c181b73d0928897cefa17dc75d7abc6e37730d5908f/pybase64-1.4.2-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fb794502b4b1ec91c4ca5d283ae71aef65e3de7721057bd9e2b3ec79f7a62d7d", size = 38230, upload-time = "2025-07-27T13:03:41.637Z" }, - { url = "https://files.pythonhosted.org/packages/4d/58/a3307b048d799ff596a3c7c574fcba66f9b6b8c899a3c00a698124ca7ad5/pybase64-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d5c532b03fd14a5040d6cf6571299a05616f925369c72ddf6fe2fb643eb36fed", size = 38319, upload-time = "2025-07-27T13:03:42.847Z" }, - { url = "https://files.pythonhosted.org/packages/08/a7/0bda06341b0a2c830d348c6e1c4d348caaae86c53dc9a046e943467a05e9/pybase64-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f699514dc1d5689ca9cf378139e0214051922732f9adec9404bc680a8bef7c0", size = 31655, upload-time = "2025-07-27T13:03:44.426Z" }, - { url = "https://files.pythonhosted.org/packages/87/df/e1d6e8479e0c5113c2c63c7b44886935ce839c2d99884c7304ca9e86547c/pybase64-1.4.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:cd3e8713cbd32c8c6aa935feaf15c7670e2b7e8bfe51c24dc556811ebd293a29", size = 68232, upload-time = "2025-07-27T13:03:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/ab/db4dbdfccb9ca874d6ce34a0784761471885d96730de85cee3d300381529/pybase64-1.4.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d377d48acf53abf4b926c2a7a24a19deb092f366a04ffd856bf4b3aa330b025d", size = 71608, upload-time = "2025-07-27T13:03:47.01Z" }, - { url = "https://files.pythonhosted.org/packages/11/e9/508df958563951045d728bbfbd3be77465f9231cf805cb7ccaf6951fc9f1/pybase64-1.4.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d83c076e78d619b9e1dd674e2bf5fb9001aeb3e0b494b80a6c8f6d4120e38cd9", size = 59912, upload-time = "2025-07-27T13:03:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/f2/58/7f2cef1ceccc682088958448d56727369de83fa6b29148478f4d2acd107a/pybase64-1.4.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:ab9cdb6a8176a5cb967f53e6ad60e40c83caaa1ae31c5e1b29e5c8f507f17538", size = 56413, upload-time = "2025-07-27T13:03:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/08/7c/7e0af5c5728fa7e2eb082d88eca7c6bd17429be819d58518e74919d42e66/pybase64-1.4.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:adf0c103ad559dbfb9fe69edfd26a15c65d9c991a5ab0a25b04770f9eb0b9484", size = 59311, upload-time = "2025-07-27T13:03:51.238Z" }, - { url = "https://files.pythonhosted.org/packages/03/8b/09825d0f37e45b9a3f546e5f990b6cf2dd838e54ea74122c2464646e0c77/pybase64-1.4.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:0d03ef2f253d97ce0685d3624bf5e552d716b86cacb8a6c971333ba4b827e1fc", size = 60282, upload-time = "2025-07-27T13:03:52.56Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3f/3711d2413f969bfd5b9cc19bc6b24abae361b7673ff37bcb90c43e199316/pybase64-1.4.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:e565abf906efee76ae4be1aef5df4aed0fda1639bc0d7732a3dafef76cb6fc35", size = 54845, upload-time = "2025-07-27T13:03:54.167Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3c/4c7ce1ae4d828c2bb56d144322f81bffbaaac8597d35407c3d7cbb0ff98f/pybase64-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3c6a5f15fd03f232fc6f295cce3684f7bb08da6c6d5b12cc771f81c9f125cc6", size = 58615, upload-time = "2025-07-27T13:03:55.494Z" }, - { url = "https://files.pythonhosted.org/packages/f5/8f/c2fc03bf4ed038358620065c75968a30184d5d3512d09d3ef9cc3bd48592/pybase64-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bad9e3db16f448728138737bbd1af9dc2398efd593a8bdd73748cc02cd33f9c6", size = 52434, upload-time = "2025-07-27T13:03:56.808Z" }, - { url = "https://files.pythonhosted.org/packages/e2/0a/757d6df0a60327c893cfae903e15419914dd792092dc8cc5c9523d40bc9b/pybase64-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2683ef271328365c31afee0ed8fa29356fb8fb7c10606794656aa9ffb95e92be", size = 68824, upload-time = "2025-07-27T13:03:58.735Z" }, - { url = "https://files.pythonhosted.org/packages/a0/14/84abe2ed8c29014239be1cfab45dfebe5a5ca779b177b8b6f779bd8b69da/pybase64-1.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:265b20089cd470079114c09bb74b101b3bfc3c94ad6b4231706cf9eff877d570", size = 57898, upload-time = "2025-07-27T13:04:00.379Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c6/d193031f90c864f7b59fa6d1d1b5af41f0f5db35439988a8b9f2d1b32a13/pybase64-1.4.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e53173badead10ef8b839aa5506eecf0067c7b75ad16d9bf39bc7144631f8e67", size = 54319, upload-time = "2025-07-27T13:04:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/cb/37/ec0c7a610ff8f994ee6e0c5d5d66b6b6310388b96ebb347b03ae39870fdf/pybase64-1.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5823b8dcf74da7da0f761ed60c961e8928a6524e520411ad05fe7f9f47d55b40", size = 56472, upload-time = "2025-07-27T13:04:03.089Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5a/e585b74f85cedd261d271e4c2ef333c5cfce7e80750771808f56fee66b98/pybase64-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1237f66c54357d325390da60aa5e21c6918fbcd1bf527acb9c1f4188c62cb7d5", size = 70966, upload-time = "2025-07-27T13:04:04.361Z" }, - { url = "https://files.pythonhosted.org/packages/ad/20/1b2fdd98b4ba36008419668c813025758214c543e362c66c49214ecd1127/pybase64-1.4.2-cp313-cp313-win32.whl", hash = "sha256:b0b851eb4f801d16040047f6889cca5e9dfa102b3e33f68934d12511245cef86", size = 33681, upload-time = "2025-07-27T13:04:06.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/64/3df4067d169c047054889f34b5a946cbe3785bca43404b93c962a5461a41/pybase64-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:19541c6e26d17d9522c02680fe242206ae05df659c82a657aabadf209cd4c6c7", size = 35822, upload-time = "2025-07-27T13:04:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fd/db505188adf812e60ee923f196f9deddd8a1895b2b29b37f5db94afc3b1c/pybase64-1.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:77a191863d576c0a5dd81f8a568a5ca15597cc980ae809dce62c717c8d42d8aa", size = 30899, upload-time = "2025-07-27T13:04:09.062Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/5f5fecd206ec1e06e1608a380af18dcb76a6ab08ade6597a3251502dcdb2/pybase64-1.4.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2e194bbabe3fdf9e47ba9f3e157394efe0849eb226df76432126239b3f44992c", size = 38677, upload-time = "2025-07-27T13:04:10.334Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0f/abe4b5a28529ef5f74e8348fa6a9ef27d7d75fbd98103d7664cf485b7d8f/pybase64-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:39aef1dadf4a004f11dd09e703abaf6528a87c8dbd39c448bb8aebdc0a08c1be", size = 32066, upload-time = "2025-07-27T13:04:11.641Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7e/ea0ce6a7155cada5526017ec588b6d6185adea4bf9331565272f4ef583c2/pybase64-1.4.2-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:91cb920c7143e36ec8217031282c8651da3b2206d70343f068fac0e7f073b7f9", size = 72300, upload-time = "2025-07-27T13:04:12.969Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/e64c7a056c9ec48dfe130d1295e47a8c2b19c3984488fc08e5eaa1e86c88/pybase64-1.4.2-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6958631143fb9e71f9842000da042ec2f6686506b6706e2dfda29e97925f6aa0", size = 75520, upload-time = "2025-07-27T13:04:14.374Z" }, - { url = "https://files.pythonhosted.org/packages/43/e0/e5f93b2e1cb0751a22713c4baa6c6eaf5f307385e369180486c8316ed21e/pybase64-1.4.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc35f14141ef3f1ac70d963950a278a2593af66fe5a1c7a208e185ca6278fa25", size = 65384, upload-time = "2025-07-27T13:04:16.204Z" }, - { url = "https://files.pythonhosted.org/packages/ff/23/8c645a1113ad88a1c6a3d0e825e93ef8b74ad3175148767853a0a4d7626e/pybase64-1.4.2-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5d949d2d677859c3a8507e1b21432a039d2b995e0bd3fe307052b6ded80f207a", size = 60471, upload-time = "2025-07-27T13:04:17.947Z" }, - { url = "https://files.pythonhosted.org/packages/8b/81/edd0f7d8b0526b91730a0dd4ce6b4c8be2136cd69d424afe36235d2d2a06/pybase64-1.4.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:09caacdd3e15fe7253a67781edd10a6a918befab0052a2a3c215fe5d1f150269", size = 63945, upload-time = "2025-07-27T13:04:19.383Z" }, - { url = "https://files.pythonhosted.org/packages/a5/a5/edc224cd821fd65100b7af7c7e16b8f699916f8c0226c9c97bbae5a75e71/pybase64-1.4.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:e44b0e793b23f28ea0f15a9754bd0c960102a2ac4bccb8fafdedbd4cc4d235c0", size = 64858, upload-time = "2025-07-27T13:04:20.807Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/92853f968f1af7e42b7e54d21bdd319097b367e7dffa2ca20787361df74c/pybase64-1.4.2-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:849f274d0bcb90fc6f642c39274082724d108e41b15f3a17864282bd41fc71d5", size = 58557, upload-time = "2025-07-27T13:04:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/76/09/0ec6bd2b2303b0ea5c6da7535edc9a608092075ef8c0cdd96e3e726cd687/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:528dba7ef1357bd7ce1aea143084501f47f5dd0fff7937d3906a68565aa59cfe", size = 63624, upload-time = "2025-07-27T13:04:23.952Z" }, - { url = "https://files.pythonhosted.org/packages/73/6e/52cb1ced2a517a3118b2e739e9417432049013ac7afa15d790103059e8e4/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:1da54be743d9a68671700cfe56c3ab8c26e8f2f5cc34eface905c55bc3a9af94", size = 56174, upload-time = "2025-07-27T13:04:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/5b/9d/820fe79347467e48af985fe46180e1dd28e698ade7317bebd66de8a143f5/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9b07c0406c3eaa7014499b0aacafb21a6d1146cfaa85d56f0aa02e6d542ee8f3", size = 72640, upload-time = "2025-07-27T13:04:26.824Z" }, - { url = "https://files.pythonhosted.org/packages/53/58/e863e10d08361e694935c815b73faad7e1ab03f99ae154d86c4e2f331896/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:312f2aa4cf5d199a97fbcaee75d2e59ebbaafcd091993eb373b43683498cdacb", size = 62453, upload-time = "2025-07-27T13:04:28.562Z" }, - { url = "https://files.pythonhosted.org/packages/95/f0/c392c4ac8ccb7a34b28377c21faa2395313e3c676d76c382642e19a20703/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad59362fc267bf15498a318c9e076686e4beeb0dfe09b457fabbc2b32468b97a", size = 58103, upload-time = "2025-07-27T13:04:29.996Z" }, - { url = "https://files.pythonhosted.org/packages/32/30/00ab21316e7df8f526aa3e3dc06f74de6711d51c65b020575d0105a025b2/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:01593bd064e7dcd6c86d04e94e44acfe364049500c20ac68ca1e708fbb2ca970", size = 60779, upload-time = "2025-07-27T13:04:31.549Z" }, - { url = "https://files.pythonhosted.org/packages/a6/65/114ca81839b1805ce4a2b7d58bc16e95634734a2059991f6382fc71caf3e/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5b81547ad8ea271c79fdf10da89a1e9313cb15edcba2a17adf8871735e9c02a0", size = 74684, upload-time = "2025-07-27T13:04:32.976Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/aa9d445b9bb693b8f6bb1456bd6d8576d79b7a63bf6c69af3a539235b15f/pybase64-1.4.2-cp313-cp313t-win32.whl", hash = "sha256:7edbe70b5654545a37e6e6b02de738303b1bbdfcde67f6cfec374cfb5cc4099e", size = 33961, upload-time = "2025-07-27T13:04:34.806Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e5/da37cfb173c646fd4fc7c6aae2bc41d40de2ee49529854af8f4e6f498b45/pybase64-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:385690addf87c25d6366fab5d8ff512eed8a7ecb18da9e8152af1c789162f208", size = 36199, upload-time = "2025-07-27T13:04:36.223Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/1eb68fb7d00f2cec8bd9838e2a30d183d6724ae06e745fd6e65216f170ff/pybase64-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c2070d0aa88580f57fe15ca88b09f162e604d19282915a95a3795b5d3c1c05b5", size = 31221, upload-time = "2025-07-27T13:04:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/32/34/b67371f4fcedd5e2def29b1cf92a4311a72f590c04850f370c75297b48ce/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:b4eed40a5f1627ee65613a6ac834a33f8ba24066656f569c852f98eb16f6ab5d", size = 38667, upload-time = "2025-07-27T13:07:25.315Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3e/e57fe09ed1c7e740d21c37023c5f7c8963b4c36380f41d10261cc76f93b4/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:57885fa521e9add235af4db13e9e048d3a2934cd27d7c5efac1925e1b4d6538d", size = 32094, upload-time = "2025-07-27T13:07:28.235Z" }, - { url = "https://files.pythonhosted.org/packages/51/34/f40d3262c3953814b9bcdcf858436bd5bc1133a698be4bcc7ed2a8c0730d/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eef9255d926c64e2fca021d3aee98023bacb98e1518e5986d6aab04102411b04", size = 43212, upload-time = "2025-07-27T13:07:31.327Z" }, - { url = "https://files.pythonhosted.org/packages/8c/2a/5e05d25718cb8ffd68bd46553ddfd2b660893d937feda1716b8a3b21fb38/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89614ea2d2329b6708746c540e0f14d692125df99fb1203ff0de948d9e68dfc9", size = 35789, upload-time = "2025-07-27T13:07:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/d5/9d/f56c3ee6e94faaae2896ecaf666428330cb24096abf7d2427371bb2b403a/pybase64-1.4.2-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:e401cecd2d7ddcd558768b2140fd4430746be4d17fb14c99eec9e40789df136d", size = 35861, upload-time = "2025-07-27T13:07:37.099Z" }, - { url = "https://files.pythonhosted.org/packages/fb/04/bfe2bd0d76385750f3541724b4abfe4ea111b3cc01ff7e83f410054adc30/pybase64-1.4.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4b29c93414ba965777643a9d98443f08f76ac04519ad717aa859113695372a07", size = 38226, upload-time = "2025-07-27T13:07:40.121Z" }, - { url = "https://files.pythonhosted.org/packages/22/13/c717855760b78ded1a9d308984c7e3e99fcf79c6cac5a231ed8c1238218f/pybase64-1.4.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e0c3353c0bf099c5c3f8f750202c486abee8f23a566b49e9e7b1222fbf5f259", size = 31524, upload-time = "2025-07-27T13:07:43.946Z" }, - { url = "https://files.pythonhosted.org/packages/cf/da/2b7e69abfc62abe4d54b10d1e09ec78021a6b9b2d7e6e7b632243a19433e/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4f98c5c6152d3c01d933fcde04322cd9ddcf65b5346034aac69a04c1a7cbb012", size = 40667, upload-time = "2025-07-27T13:07:46.715Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/ba738655fb3ba85c7a0605eddd2709fef606e654840c72ee5c5ff7ab29bf/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9096a4977b7aff7ef250f759fb6a4b6b7b6199d99c84070c7fc862dd3b208b34", size = 41290, upload-time = "2025-07-27T13:07:49.534Z" }, - { url = "https://files.pythonhosted.org/packages/5d/38/2d5502fcaf712297b95c1b6ca924656dd7d17501fd7f9c9e0b3bbf8892ef/pybase64-1.4.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49d8597e2872966399410502310b1e2a5b7e8d8ba96766ee1fe242e00bd80775", size = 35438, upload-time = "2025-07-27T13:07:52.327Z" }, - { url = "https://files.pythonhosted.org/packages/b6/db/e03b8b6daa60a3fbef21741403e0cf18b2aff3beebdf6e3596bb9bab16c7/pybase64-1.4.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ef16366565389a287df82659e055e88bdb6c36e46a3394950903e0a9cb2e5bf", size = 36121, upload-time = "2025-07-27T13:07:55.54Z" }, - { url = "https://files.pythonhosted.org/packages/0e/bf/5ebaa2d9ddb5fc506633bc8b820fc27e64da964937fb30929c0367c47d00/pybase64-1.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0a5393be20b0705870f5a8969749af84d734c077de80dd7e9f5424a247afa85e", size = 38162, upload-time = "2025-07-27T13:07:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/795c5fd6e5571bb675bf9add8a048166dddf8951c2a903fea8557743886b/pybase64-1.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:448f0259a2f1a17eb086f70fe2ad9b556edba1fc5bc4e62ce6966179368ee9f8", size = 31452, upload-time = "2025-07-27T13:08:01.259Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/c819003b59b2832256b72ad23cbeadbd95d083ef0318d07149a58b7a88af/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1159e70cba8e76c3d8f334bd1f8fd52a1bb7384f4c3533831b23ab2df84a6ef3", size = 40668, upload-time = "2025-07-27T13:08:04.176Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/38c6aba28678c4a4db49312a6b8171b93a0ffe9f21362cf4c0f325caa850/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d943bc5dad8388971494554b97f22ae06a46cc7779ad0de3d4bfdf7d0bbea30", size = 41281, upload-time = "2025-07-27T13:08:07.395Z" }, - { url = "https://files.pythonhosted.org/packages/e5/23/5927bd9e59714e4e8cefd1d21ccd7216048bb1c6c3e7104b1b200afdc63d/pybase64-1.4.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10b99182c561d86422c5de4265fd1f8f172fb38efaed9d72c71fb31e279a7f94", size = 35433, upload-time = "2025-07-27T13:08:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/01/0f/fab7ed5bf4926523c3b39f7621cea3e0da43f539fbc2270e042f1afccb79/pybase64-1.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bb082c1114f046e59fcbc4f2be13edc93b36d7b54b58605820605be948f8fdf6", size = 36131, upload-time = "2025-07-27T13:08:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/16d7af6fae7803f4c691856bc0d8d433ccf30e106432e2ef7707ee19a38a/pybase64-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f63aa7f29139b8a05ce5f97cdb7fad63d29071e5bdc8a638a343311fe996112a", size = 38241, upload-time = "2025-12-06T13:22:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/4d/3e/268beb8d2240ab55396af4d1b45d2494935982212549b92a5f5b57079bd3/pybase64-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5943ec1ae87a8b4fe310905bb57205ea4330c75e2c628433a7d9dd52295b588", size = 31672, upload-time = "2025-12-06T13:22:28.854Z" }, + { url = "https://files.pythonhosted.org/packages/80/14/4365fa33222edcc46b6db4973f9e22bda82adfb6ab2a01afff591f1e41c8/pybase64-1.4.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5f2b8aef86f35cd5894c13681faf433a1fffc5b2e76544dcb5416a514a1a8347", size = 65978, upload-time = "2025-12-06T13:22:30.191Z" }, + { url = "https://files.pythonhosted.org/packages/1c/22/e89739d8bc9b96c68ead44b4eec42fe555683d9997e4ba65216d384920fc/pybase64-1.4.3-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ec7e53dd09b0a8116ccf5c3265c7c7fce13c980747525be76902aef36a514a", size = 68903, upload-time = "2025-12-06T13:22:31.29Z" }, + { url = "https://files.pythonhosted.org/packages/77/e1/7e59a19f8999cdefe9eb0d56bfd701dd38263b0f6fb4a4d29fce165a1b36/pybase64-1.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7528604cd69c538e1dbaafded46e9e4915a2adcd6f2a60fcef6390d87ca922ea", size = 57516, upload-time = "2025-12-06T13:22:32.395Z" }, + { url = "https://files.pythonhosted.org/packages/42/ad/f47dc7e6fe32022b176868b88b671a32dab389718c8ca905cab79280aaaf/pybase64-1.4.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:4ec645f32b50593879031e09158f8681a1db9f5df0f72af86b3969a1c5d1fa2b", size = 54533, upload-time = "2025-12-06T13:22:33.457Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/7ab312b5a324833953b00e47b23eb4f83d45bd5c5c854b4b4e51b2a0cf5b/pybase64-1.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:634a000c5b3485ccc18bb9b244e0124f74b6fbc7f43eade815170237a7b34c64", size = 57187, upload-time = "2025-12-06T13:22:34.566Z" }, + { url = "https://files.pythonhosted.org/packages/2c/84/80acab1fcbaaae103e6b862ef5019192c8f2cd8758433595a202179a0d1d/pybase64-1.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:309ea32ad07639a485580af1be0ad447a434deb1924e76adced63ac2319cfe15", size = 57730, upload-time = "2025-12-06T13:22:35.581Z" }, + { url = "https://files.pythonhosted.org/packages/1f/24/84256d472400ea3163d7d69c44bb7e2e1027f0f1d4d20c47629a7dc4578e/pybase64-1.4.3-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:d10d517566b748d3f25f6ac7162af779360c1c6426ad5f962927ee205990d27c", size = 53036, upload-time = "2025-12-06T13:22:36.621Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0f/33aecbed312ee0431798a73fa25e00dedbffdd91389ee23121fed397c550/pybase64-1.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a74cc0f4d835400857cc5c6d27ec854f7949491e07a04e6d66e2137812831f4c", size = 56321, upload-time = "2025-12-06T13:22:37.7Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1c/a341b050746658cbec8cab3c733aeb3ef52ce8f11e60d0d47adbdf729ebf/pybase64-1.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1b591d774ac09d5eb73c156a03277cb271438fbd8042bae4109ff3a827cd218c", size = 50114, upload-time = "2025-12-06T13:22:38.752Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d3/f7e6680ae6dc4ddff39112ad66e0fa6b2ec346e73881bafc08498c560bc0/pybase64-1.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5eb588d35a04302ef6157d17db62354a787ac6f8b1585dd0b90c33d63a97a550", size = 66570, upload-time = "2025-12-06T13:22:40.221Z" }, + { url = "https://files.pythonhosted.org/packages/4c/71/774748eecc7fe23869b7e5df028e3c4c2efa16b506b83ea3fa035ea95dc2/pybase64-1.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df8b122d5be2c96962231cc4831d9c2e1eae6736fb12850cec4356d8b06fe6f8", size = 55700, upload-time = "2025-12-06T13:22:41.289Z" }, + { url = "https://files.pythonhosted.org/packages/b3/91/dd15075bb2fe0086193e1cd4bad80a43652c38d8a572f9218d46ba721802/pybase64-1.4.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:31b7a85c661fc591bbcce82fb8adaebe2941e6a83b08444b0957b77380452a4b", size = 52491, upload-time = "2025-12-06T13:22:42.628Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/f357d63ea3774c937fc47160e040419ed528827aa3d4306d5ec9826259c0/pybase64-1.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e6d7beaae65979fef250e25e66cf81c68a8f81910bcda1a2f43297ab486a7e4e", size = 53957, upload-time = "2025-12-06T13:22:44.615Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c3/243693771701a54e67ff5ccbf4c038344f429613f5643169a7befc51f007/pybase64-1.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4a6276bc3a3962d172a2b5aba544d89881c4037ea954517b86b00892c703d007", size = 68422, upload-time = "2025-12-06T13:22:45.641Z" }, + { url = "https://files.pythonhosted.org/packages/75/95/f987081bf6bc1d1eda3012dae1b06ad427732ef9933a632cb8b58f9917f8/pybase64-1.4.3-cp310-cp310-win32.whl", hash = "sha256:4bdd07ef017515204ee6eaab17e1ad05f83c0ccb5af8ae24a0fe6d9cb5bb0b7a", size = 33622, upload-time = "2025-12-06T13:22:47.348Z" }, + { url = "https://files.pythonhosted.org/packages/79/28/c169a769fe90128f16d394aad87b2096dd4bf2f035ae0927108a46b617df/pybase64-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:5db0b6bbda15110db2740c61970a8fda3bf9c93c3166a3f57f87c7865ed1125c", size = 35799, upload-time = "2025-12-06T13:22:48.731Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f2/bdbe6af0bd4f3fe5bc70e77ead7f7d523bb9d3ca3ad50ac42b9adbb9ca14/pybase64-1.4.3-cp310-cp310-win_arm64.whl", hash = "sha256:f96367dfc82598569aa02b1103ebd419298293e59e1151abda2b41728703284b", size = 31158, upload-time = "2025-12-06T13:22:50.021Z" }, + { url = "https://files.pythonhosted.org/packages/2b/63/21e981e9d3f1f123e0b0ee2130112b1956cad9752309f574862c7ae77c08/pybase64-1.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:70b0d4a4d54e216ce42c2655315378b8903933ecfa32fced453989a92b4317b2", size = 38237, upload-time = "2025-12-06T13:22:52.159Z" }, + { url = "https://files.pythonhosted.org/packages/92/fb/3f448e139516404d2a3963915cc10dc9dde7d3a67de4edba2f827adfef17/pybase64-1.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8127f110cdee7a70e576c5c9c1d4e17e92e76c191869085efbc50419f4ae3c72", size = 31673, upload-time = "2025-12-06T13:22:53.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/bb06a5b9885e7d853ac1e801c4d8abfdb4c8506deee33e53d55aa6690e67/pybase64-1.4.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f9ef0388878bc15a084bd9bf73ec1b2b4ee513d11009b1506375e10a7aae5032", size = 68331, upload-time = "2025-12-06T13:22:54.197Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/8d60b9ec5e658185fc2ee3333e01a6e30d717cf677b24f47cbb3a859d13c/pybase64-1.4.3-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95a57cccf106352a72ed8bc8198f6820b16cc7d55aa3867a16dea7011ae7c218", size = 71370, upload-time = "2025-12-06T13:22:55.517Z" }, + { url = "https://files.pythonhosted.org/packages/ac/29/a3e5c1667cc8c38d025a4636855de0fc117fc62e2afeb033a3c6f12c6a22/pybase64-1.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cd1c47dfceb9c7bd3de210fb4e65904053ed2d7c9dce6d107f041ff6fbd7e21", size = 59834, upload-time = "2025-12-06T13:22:56.682Z" }, + { url = "https://files.pythonhosted.org/packages/a9/00/8ffcf9810bd23f3984698be161cf7edba656fd639b818039a7be1d6405d4/pybase64-1.4.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9fe9922698f3e2f72874b26890d53a051c431d942701bb3a37aae94da0b12107", size = 56652, upload-time = "2025-12-06T13:22:57.724Z" }, + { url = "https://files.pythonhosted.org/packages/81/62/379e347797cdea4ab686375945bc77ad8d039c688c0d4d0cfb09d247beb9/pybase64-1.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:af5f4bd29c86b59bb4375e0491d16ec8a67548fa99c54763aaedaf0b4b5a6632", size = 59382, upload-time = "2025-12-06T13:22:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/c6/f2/9338ffe2f487086f26a2c8ca175acb3baa86fce0a756ff5670a0822bb877/pybase64-1.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c302f6ca7465262908131411226e02100f488f531bb5e64cb901aa3f439bccd9", size = 59990, upload-time = "2025-12-06T13:23:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/85a6142b65b4df8625b337727aa81dc199642de3d09677804141df6ee312/pybase64-1.4.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2f3f439fa4d7fde164ebbbb41968db7d66b064450ab6017c6c95cef0afa2b349", size = 54923, upload-time = "2025-12-06T13:23:02.369Z" }, + { url = "https://files.pythonhosted.org/packages/ac/00/e40215d25624012bf5b7416ca37f168cb75f6dd15acdb91ea1f2ea4dc4e7/pybase64-1.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a23c6866551043f8b681a5e1e0d59469148b2920a3b4fc42b1275f25ea4217a", size = 58664, upload-time = "2025-12-06T13:23:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/d7e19a63e795c13837f2356268d95dc79d1180e756f57ced742a1e52fdeb/pybase64-1.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:56e6526f8565642abc5f84338cc131ce298a8ccab696b19bdf76fa6d7dc592ef", size = 52338, upload-time = "2025-12-06T13:23:04.458Z" }, + { url = "https://files.pythonhosted.org/packages/f2/32/3c746d7a310b69bdd9df77ffc85c41b80bce00a774717596f869b0d4a20e/pybase64-1.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6a792a8b9d866ffa413c9687d9b611553203753987a3a582d68cbc51cf23da45", size = 68993, upload-time = "2025-12-06T13:23:05.526Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b3/63cec68f9d6f6e4c0b438d14e5f1ef536a5fe63ce14b70733ac5e31d7ab8/pybase64-1.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:62ad29a5026bb22cfcd1ca484ec34b0a5ced56ddba38ceecd9359b2818c9c4f9", size = 58055, upload-time = "2025-12-06T13:23:06.931Z" }, + { url = "https://files.pythonhosted.org/packages/d5/cb/7acf7c3c06f9692093c07f109668725dc37fb9a3df0fa912b50add645195/pybase64-1.4.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11b9d1d2d32ec358c02214363b8fc3651f6be7dd84d880ecd597a6206a80e121", size = 54430, upload-time = "2025-12-06T13:23:07.936Z" }, + { url = "https://files.pythonhosted.org/packages/33/39/4eb33ff35d173bfff4002e184ce8907f5d0a42d958d61cd9058ef3570179/pybase64-1.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0aebaa7f238caa0a0d373616016e2040c6c879ebce3ba7ab3c59029920f13640", size = 56272, upload-time = "2025-12-06T13:23:09.253Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/a76d65c375a254e65b730c6f56bf528feca91305da32eceab8bcc08591e6/pybase64-1.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e504682b20c63c2b0c000e5f98a80ea867f8d97642e042a5a39818e44ba4d599", size = 70904, upload-time = "2025-12-06T13:23:10.336Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/8338b6d3da3c265002839e92af0a80d6db88385c313c73f103dfb800c857/pybase64-1.4.3-cp311-cp311-win32.whl", hash = "sha256:e9a8b81984e3c6fb1db9e1614341b0a2d98c0033d693d90c726677db1ffa3a4c", size = 33639, upload-time = "2025-12-06T13:23:11.9Z" }, + { url = "https://files.pythonhosted.org/packages/39/dc/32efdf2f5927e5449cc341c266a1bbc5fecd5319a8807d9c5405f76e6d02/pybase64-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:a90a8fa16a901fabf20de824d7acce07586e6127dc2333f1de05f73b1f848319", size = 35797, upload-time = "2025-12-06T13:23:13.174Z" }, + { url = "https://files.pythonhosted.org/packages/da/59/eda4f9cb0cbce5a45f0cd06131e710674f8123a4d570772c5b9694f88559/pybase64-1.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:61d87de5bc94d143622e94390ec3e11b9c1d4644fe9be3a81068ab0f91056f59", size = 31160, upload-time = "2025-12-06T13:23:15.696Z" }, + { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, + { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, + { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, + { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, + { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, + { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, + { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, + { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, + { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, + { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, + { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, + { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, + { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, + { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, + { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, + { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, + { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, + { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, + { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, + { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/545fd4935a0e1ddd7147f557bf8157c73eecec9cffd523382fa7af2557de/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:d27c1dfdb0c59a5e758e7a98bd78eaca5983c22f4a811a36f4f980d245df4611", size = 38393, upload-time = "2025-12-06T13:26:19.535Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ca/ae7a96be9ddc96030d4e9dffc43635d4e136b12058b387fd47eb8301b60f/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0f1a0c51d6f159511e3431b73c25db31095ee36c394e26a4349e067c62f434e5", size = 32109, upload-time = "2025-12-06T13:26:20.72Z" }, + { url = "https://files.pythonhosted.org/packages/bf/44/d4b7adc7bf4fd5b52d8d099121760c450a52c390223806b873f0b6a2d551/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a492518f3078a4e3faaef310697d21df9c6bc71908cebc8c2f6fbfa16d7d6b1f", size = 43227, upload-time = "2025-12-06T13:26:21.845Z" }, + { url = "https://files.pythonhosted.org/packages/08/86/2ba2d8734ef7939debeb52cf9952e457ba7aa226cae5c0e6dd631f9b851f/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae1a0f47784fd16df90d8acc32011c8d5fcdd9ab392c9ec49543e5f6a9c43a4", size = 35804, upload-time = "2025-12-06T13:26:23.149Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5b/19c725dc3aaa6281f2ce3ea4c1628d154a40dd99657d1381995f8096768b/pybase64-1.4.3-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:03cea70676ffbd39a1ab7930a2d24c625b416cacc9d401599b1d29415a43ab6a", size = 35880, upload-time = "2025-12-06T13:26:24.663Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/cf/6e712491bd665ea8633efb0b484121893ea838d8e830e06f39f2aae37e58/pybase64-1.4.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94cf50c36bb2f8618982ee5a978c4beed9db97d35944fa96e8586dd953c7994a", size = 38007, upload-time = "2025-12-06T13:26:32.804Z" }, + { url = "https://files.pythonhosted.org/packages/38/c0/9272cae1c49176337dcdbd97511e2843faae1aaf5a5fb48569093c6cd4ce/pybase64-1.4.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:01bc3ff5ca1341685c6d2d945b035f442f7b9c3b068a5c6ee8408a41fda5754e", size = 31538, upload-time = "2025-12-06T13:26:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/20/f2/17546f97befe429c73f622bbd869ceebb518c40fdb0dec4c4f98312e80a5/pybase64-1.4.3-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:03d0aa3761a99034960496280c02aa063f856a3cc9b33771bc4eab0e4e72b5c2", size = 40682, upload-time = "2025-12-06T13:26:35.168Z" }, + { url = "https://files.pythonhosted.org/packages/92/a0/464b36d5dfb61f3da17858afaeaa876a9342d58e9f17803ce7f28b5de9e8/pybase64-1.4.3-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7ca5b1ce768520acd6440280cdab35235b27ad2faacfcec064bc9c3377066ef1", size = 41306, upload-time = "2025-12-06T13:26:36.351Z" }, + { url = "https://files.pythonhosted.org/packages/07/c9/a748dfc0969a8d960ecf1e82c8a2a16046ffec22f8e7ece582aa3b1c6cf9/pybase64-1.4.3-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3caa1e2ddad1c50553ffaaa1c86b74b3f9fbd505bea9970326ab88fc68c4c184", size = 35452, upload-time = "2025-12-06T13:26:37.772Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/4d37bd3577d1aa6c732dc099087fe027c48873e223de3784b095e5653f8b/pybase64-1.4.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bd47076f736b27a8b0f9b30d93b6bb4f5af01b0dc8971f883ed3b75934f39a99", size = 36125, upload-time = "2025-12-06T13:26:39.78Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/160dded493c00d3376d4ad0f38a2119c5345de4a6693419ad39c3565959b/pybase64-1.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:277de6e03cc9090fb359365c686a2a3036d23aee6cd20d45d22b8c89d1247f17", size = 37939, upload-time = "2025-12-06T13:26:41.014Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/a0f10be8d648d6f8f26e560d6e6955efa7df0ff1e009155717454d76f601/pybase64-1.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab1dd8b1ed2d1d750260ed58ab40defaa5ba83f76a30e18b9ebd5646f6247ae5", size = 31466, upload-time = "2025-12-06T13:26:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/22/832a2f9e76cdf39b52e01e40d8feeb6a04cf105494f2c3e3126d0149717f/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:bd4d2293de9fd212e294c136cec85892460b17d24e8c18a6ba18750928037750", size = 40681, upload-time = "2025-12-06T13:26:43.782Z" }, + { url = "https://files.pythonhosted.org/packages/12/d7/6610f34a8972415fab3bb4704c174a1cc477bffbc3c36e526428d0f3957d/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af6d0d3a691911cc4c9a625f3ddcd3af720738c21be3d5c72de05629139d393", size = 41294, upload-time = "2025-12-06T13:26:44.936Z" }, + { url = "https://files.pythonhosted.org/packages/64/25/ed24400948a6c974ab1374a233cb7e8af0a5373cea0dd8a944627d17c34a/pybase64-1.4.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfc8c49a28322d82242088378f8542ce97459866ba73150b062a7073e82629d", size = 35447, upload-time = "2025-12-06T13:26:46.098Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2b/e18ee7c5ee508a82897f021c1981533eca2940b5f072fc6ed0906c03a7a7/pybase64-1.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:debf737e09b8bf832ba86f5ecc3d3dbd0e3021d6cd86ba4abe962d6a5a77adb3", size = 36134, upload-time = "2025-12-06T13:26:47.35Z" }, ] [[package]] @@ -6045,27 +6073,27 @@ wheels = [ [[package]] name = "pynacl" -version = "1.6.0" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/c6/a3124dee667a423f2c637cfd262a54d67d8ccf3e160f3c50f622a85b7723/pynacl-1.6.0.tar.gz", hash = "sha256:cb36deafe6e2bce3b286e5d1f3e1c246e0ccdb8808ddb4550bb2792f2df298f2", size = 3505641, upload-time = "2025-09-10T23:39:22.308Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/46/aeca065d227e2265125aea590c9c47fbf5786128c9400ee0eb7c88931f06/pynacl-1.6.1.tar.gz", hash = "sha256:8d361dac0309f2b6ad33b349a56cd163c98430d409fa503b10b70b3ad66eaa1d", size = 3506616, upload-time = "2025-11-10T16:02:13.195Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/37/87c72df19857c5b3b47ace6f211a26eb862ada495cc96daa372d96048fca/pynacl-1.6.0-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:f4b3824920e206b4f52abd7de621ea7a44fd3cb5c8daceb7c3612345dfc54f2e", size = 382610, upload-time = "2025-09-10T23:38:49.459Z" }, - { url = "https://files.pythonhosted.org/packages/0c/64/3ce958a5817fd3cc6df4ec14441c43fd9854405668d73babccf77f9597a3/pynacl-1.6.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:16dd347cdc8ae0b0f6187a2608c0af1c8b7ecbbe6b4a06bff8253c192f696990", size = 798744, upload-time = "2025-09-10T23:38:58.531Z" }, - { url = "https://files.pythonhosted.org/packages/e4/8a/3f0dd297a0a33fa3739c255feebd0206bb1df0b44c52fbe2caf8e8bc4425/pynacl-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16c60daceee88d04f8d41d0a4004a7ed8d9a5126b997efd2933e08e93a3bd850", size = 1397879, upload-time = "2025-09-10T23:39:00.44Z" }, - { url = "https://files.pythonhosted.org/packages/41/94/028ff0434a69448f61348d50d2c147dda51aabdd4fbc93ec61343332174d/pynacl-1.6.0-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25720bad35dfac34a2bcdd61d9e08d6bfc6041bebc7751d9c9f2446cf1e77d64", size = 833907, upload-time = "2025-09-10T23:38:50.936Z" }, - { url = "https://files.pythonhosted.org/packages/52/bc/a5cff7f8c30d5f4c26a07dfb0bcda1176ab8b2de86dda3106c00a02ad787/pynacl-1.6.0-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bfaa0a28a1ab718bad6239979a5a57a8d1506d0caf2fba17e524dbb409441cf", size = 1436649, upload-time = "2025-09-10T23:38:52.783Z" }, - { url = "https://files.pythonhosted.org/packages/7a/20/c397be374fd5d84295046e398de4ba5f0722dc14450f65db76a43c121471/pynacl-1.6.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ef214b90556bb46a485b7da8258e59204c244b1b5b576fb71848819b468c44a7", size = 817142, upload-time = "2025-09-10T23:38:54.4Z" }, - { url = "https://files.pythonhosted.org/packages/12/30/5efcef3406940cda75296c6d884090b8a9aad2dcc0c304daebb5ae99fb4a/pynacl-1.6.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:49c336dd80ea54780bcff6a03ee1a476be1612423010472e60af83452aa0f442", size = 1401794, upload-time = "2025-09-10T23:38:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/be/e1/a8fe1248cc17ccb03b676d80fa90763760a6d1247da434844ea388d0816c/pynacl-1.6.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f3482abf0f9815e7246d461fab597aa179b7524628a4bc36f86a7dc418d2608d", size = 772161, upload-time = "2025-09-10T23:39:01.93Z" }, - { url = "https://files.pythonhosted.org/packages/a3/76/8a62702fb657d6d9104ce13449db221a345665d05e6a3fdefb5a7cafd2ad/pynacl-1.6.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:140373378e34a1f6977e573033d1dd1de88d2a5d90ec6958c9485b2fd9f3eb90", size = 1370720, upload-time = "2025-09-10T23:39:03.531Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/9e9e9b777a1c4c8204053733e1a0269672c0bd40852908c9ad6b6eaba82c/pynacl-1.6.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6b393bc5e5a0eb86bb85b533deb2d2c815666665f840a09e0aa3362bb6088736", size = 791252, upload-time = "2025-09-10T23:39:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/63/ef/d972ce3d92ae05c9091363cf185e8646933f91c376e97b8be79ea6e96c22/pynacl-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a25cfede801f01e54179b8ff9514bd7b5944da560b7040939732d1804d25419", size = 1362910, upload-time = "2025-09-10T23:39:06.924Z" }, - { url = "https://files.pythonhosted.org/packages/35/2c/ee0b373a1861f66a7ca8bdb999331525615061320dd628527a50ba8e8a60/pynacl-1.6.0-cp38-abi3-win32.whl", hash = "sha256:dcdeb41c22ff3c66eef5e63049abf7639e0db4edee57ba70531fc1b6b133185d", size = 226461, upload-time = "2025-09-10T23:39:11.894Z" }, - { url = "https://files.pythonhosted.org/packages/75/f7/41b6c0b9dd9970173b6acc026bab7b4c187e4e5beef2756d419ad65482da/pynacl-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:cf831615cc16ba324240de79d925eacae8265b7691412ac6b24221db157f6bd1", size = 238802, upload-time = "2025-09-10T23:39:08.966Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0f/462326910c6172fa2c6ed07922b22ffc8e77432b3affffd9e18f444dbfbb/pynacl-1.6.0-cp38-abi3-win_arm64.whl", hash = "sha256:84709cea8f888e618c21ed9a0efdb1a59cc63141c403db8bf56c469b71ad56f2", size = 183846, upload-time = "2025-09-10T23:39:10.552Z" }, + { url = "https://files.pythonhosted.org/packages/49/41/3cfb3b4f3519f6ff62bf71bf1722547644bcfb1b05b8fdbdc300249ba113/pynacl-1.6.1-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:a6f9fd6d6639b1e81115c7f8ff16b8dedba1e8098d2756275d63d208b0e32021", size = 387591, upload-time = "2025-11-10T16:01:49.1Z" }, + { url = "https://files.pythonhosted.org/packages/18/21/b8a6563637799f617a3960f659513eccb3fcc655d5fc2be6e9dc6416826f/pynacl-1.6.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e49a3f3d0da9f79c1bec2aa013261ab9fa651c7da045d376bd306cf7c1792993", size = 798866, upload-time = "2025-11-10T16:01:55.688Z" }, + { url = "https://files.pythonhosted.org/packages/e8/6c/dc38033bc3ea461e05ae8f15a81e0e67ab9a01861d352ae971c99de23e7c/pynacl-1.6.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7713f8977b5d25f54a811ec9efa2738ac592e846dd6e8a4d3f7578346a841078", size = 1398001, upload-time = "2025-11-10T16:01:57.101Z" }, + { url = "https://files.pythonhosted.org/packages/9f/05/3ec0796a9917100a62c5073b20c4bce7bf0fea49e99b7906d1699cc7b61b/pynacl-1.6.1-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a3becafc1ee2e5ea7f9abc642f56b82dcf5be69b961e782a96ea52b55d8a9fc", size = 834024, upload-time = "2025-11-10T16:01:50.228Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b7/ae9982be0f344f58d9c64a1c25d1f0125c79201634efe3c87305ac7cb3e3/pynacl-1.6.1-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ce50d19f1566c391fedc8dc2f2f5be265ae214112ebe55315e41d1f36a7f0a9", size = 1436766, upload-time = "2025-11-10T16:01:51.886Z" }, + { url = "https://files.pythonhosted.org/packages/b4/51/b2ccbf89cf3025a02e044dd68a365cad593ebf70f532299f2c047d2b7714/pynacl-1.6.1-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:543f869140f67d42b9b8d47f922552d7a967e6c116aad028c9bfc5f3f3b3a7b7", size = 817275, upload-time = "2025-11-10T16:01:53.351Z" }, + { url = "https://files.pythonhosted.org/packages/a8/6c/dd9ee8214edf63ac563b08a9b30f98d116942b621d39a751ac3256694536/pynacl-1.6.1-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a2bb472458c7ca959aeeff8401b8efef329b0fc44a89d3775cffe8fad3398ad8", size = 1401891, upload-time = "2025-11-10T16:01:54.587Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c1/97d3e1c83772d78ee1db3053fd674bc6c524afbace2bfe8d419fd55d7ed1/pynacl-1.6.1-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:3206fa98737fdc66d59b8782cecc3d37d30aeec4593d1c8c145825a345bba0f0", size = 772291, upload-time = "2025-11-10T16:01:58.111Z" }, + { url = "https://files.pythonhosted.org/packages/4d/ca/691ff2fe12f3bb3e43e8e8df4b806f6384593d427f635104d337b8e00291/pynacl-1.6.1-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:53543b4f3d8acb344f75fd4d49f75e6572fce139f4bfb4815a9282296ff9f4c0", size = 1370839, upload-time = "2025-11-10T16:01:59.252Z" }, + { url = "https://files.pythonhosted.org/packages/30/27/06fe5389d30391fce006442246062cc35773c84fbcad0209fbbf5e173734/pynacl-1.6.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:319de653ef84c4f04e045eb250e6101d23132372b0a61a7acf91bac0fda8e58c", size = 791371, upload-time = "2025-11-10T16:02:01.075Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7a/e2bde8c9d39074a5aa046c7d7953401608d1f16f71e237f4bef3fb9d7e49/pynacl-1.6.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:262a8de6bba4aee8a66f5edf62c214b06647461c9b6b641f8cd0cb1e3b3196fe", size = 1363031, upload-time = "2025-11-10T16:02:02.656Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b6/63fd77264dae1087770a1bb414bc604470f58fbc21d83822fc9c76248076/pynacl-1.6.1-cp38-abi3-win32.whl", hash = "sha256:9fd1a4eb03caf8a2fe27b515a998d26923adb9ddb68db78e35ca2875a3830dde", size = 226585, upload-time = "2025-11-10T16:02:07.116Z" }, + { url = "https://files.pythonhosted.org/packages/12/c8/b419180f3fdb72ab4d45e1d88580761c267c7ca6eda9a20dcbcba254efe6/pynacl-1.6.1-cp38-abi3-win_amd64.whl", hash = "sha256:a569a4069a7855f963940040f35e87d8bc084cb2d6347428d5ad20550a0a1a21", size = 238923, upload-time = "2025-11-10T16:02:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/35/76/c34426d532e4dce7ff36e4d92cb20f4cbbd94b619964b93d24e8f5b5510f/pynacl-1.6.1-cp38-abi3-win_arm64.whl", hash = "sha256:5953e8b8cfadb10889a6e7bd0f53041a745d1b3d30111386a1bb37af171e6daf", size = 183970, upload-time = "2025-11-10T16:02:05.786Z" }, ] [[package]] @@ -6183,14 +6211,14 @@ wheels = [ [[package]] name = "pypdf" -version = "6.4.0" +version = "6.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/01/f7510cc6124f494cfbec2e8d3c2e1a20d4f6c18622b0c03a3a70e968bacb/pypdf-6.4.0.tar.gz", hash = "sha256:4769d471f8ddc3341193ecc5d6560fa44cf8cd0abfabf21af4e195cc0c224072", size = 5276661, upload-time = "2025-11-23T14:04:43.185Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/e0/57f914ae9fedbc91fe3ebe74b78c88903943ec9c232b6da15947bb3bf8ab/pypdf-6.4.1.tar.gz", hash = "sha256:36eb0b52730fc3077d2b8d4122751e696d46af9ef9e5383db492df1ab0cc4647", size = 5275322, upload-time = "2025-12-07T14:19:27.922Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/f2/9c9429411c91ac1dd5cd66780f22b6df20c64c3646cdd1e6d67cf38579c4/pypdf-6.4.0-py3-none-any.whl", hash = "sha256:55ab9837ed97fd7fcc5c131d52fcc2223bc5c6b8a1488bbf7c0e27f1f0023a79", size = 329497, upload-time = "2025-11-23T14:04:41.448Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/68c0f473d8b8764b23f199450dfa035e6f2206e67e9bde5dd695bab9bdf0/pypdf-6.4.1-py3-none-any.whl", hash = "sha256:1782ee0766f0b77defc305f1eb2bafe738a2ef6313f3f3d2ee85b4542ba7e535", size = 328325, upload-time = "2025-12-07T14:19:26.286Z" }, ] [[package]] @@ -6557,7 +6585,7 @@ dependencies = [ { name = "protobuf" }, { name = "pydantic" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/56/3f355f931c239c260b4fe3bd6433ec6c9e6185cd5ae0970fe89d0ca6daee/qdrant_client-1.14.3.tar.gz", hash = "sha256:bb899e3e065b79c04f5e47053d59176150c0a5dabc09d7f476c8ce8e52f4d281", size = 286766, upload-time = "2025-06-16T11:13:47.838Z" } wheels = [ @@ -6639,7 +6667,7 @@ wheels = [ [[package]] name = "rapidocr" -version = "3.4.2" +version = "3.4.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorlog" }, @@ -6656,7 +6684,7 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/83/5b8c8075954c5b61d938b8954710d986134c4ca7c32a841ad7d8c844cf6c/rapidocr-3.4.2-py3-none-any.whl", hash = "sha256:17845fa8cc9a20a935111e59482f2214598bba1547000cfd960d8924dd4522a5", size = 15056674, upload-time = "2025-10-11T14:43:00.296Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a8/401c97c8fa6c7f964ace3bf11d8fad4902f07f969a0f4f5d7518f46ebeef/rapidocr-3.4.3-py3-none-any.whl", hash = "sha256:a007bf196c41e2c7321dfa570e8cef06cd7fb41d7a283b91b7e4b7b08623ed27", size = 15060208, upload-time = "2025-12-09T14:45:34.189Z" }, ] [[package]] @@ -6772,7 +6800,7 @@ dependencies = [ { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ @@ -7187,7 +7215,7 @@ dependencies = [ { name = "python-dateutil" }, { name = "requests" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4c/e7/f6ed9d4259e78874dcfcc7a2f4aeb86b3035844ea73ddc430bfa0b9baab0/scrapfly_sdk-0.8.23.tar.gz", hash = "sha256:2668f7a82bf3a6b240be2f1e4090cf140d74181de57bb46543719554fbed55ae", size = 42258, upload-time = "2025-04-29T18:34:32.714Z" } wheels = [ @@ -7227,7 +7255,7 @@ wheels = [ [[package]] name = "selenium" -version = "4.38.0" +version = "4.39.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin'", @@ -7248,12 +7276,12 @@ dependencies = [ { name = "trio", marker = "platform_python_implementation != 'PyPy'" }, { name = "trio-websocket", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "platform_python_implementation != 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, extra = ["socks"], marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["socks"], marker = "platform_python_implementation != 'PyPy'" }, { name = "websocket-client", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/a0/60a5e7e946420786d57816f64536e21a29f0554706b36f3cba348107024c/selenium-4.38.0.tar.gz", hash = "sha256:c117af6727859d50f622d6d0785b945c5db3e28a45ec12ad85cee2e7cc84fc4c", size = 924101, upload-time = "2025-10-25T02:13:06.752Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/19/27c1bf9eb1f7025632d35a956b50746efb4b10aa87f961b263fa7081f4c5/selenium-4.39.0.tar.gz", hash = "sha256:12f3325f02d43b6c24030fc9602b34a3c6865abbb1db9406641d13d108aa1889", size = 928575, upload-time = "2025-12-06T23:12:34.896Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/d3/76c8f4a8d99b9f1ebcf9a611b4dd992bf5ee082a6093cfc649af3d10f35b/selenium-4.38.0-py3-none-any.whl", hash = "sha256:ed47563f188130a6fd486b327ca7ba48c5b11fb900e07d6457befdde320e35fd", size = 9694571, upload-time = "2025-10-25T02:13:04.417Z" }, + { url = "https://files.pythonhosted.org/packages/58/d0/55a6b7c6f35aad4c8a54be0eb7a52c1ff29a59542fc3e655f0ecbb14456d/selenium-4.39.0-py3-none-any.whl", hash = "sha256:c85f65d5610642ca0f47dae9d5cc117cd9e831f74038bc09fe1af126288200f9", size = 9655249, upload-time = "2025-12-06T23:12:33.085Z" }, ] [[package]] @@ -7285,7 +7313,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895, upload-time = "2025-12-03T14:06:36.846Z" } wheels = [ @@ -7475,14 +7503,13 @@ wheels = [ [[package]] name = "snowflake-connector-python" -version = "3.18.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asn1crypto" }, { name = "boto3" }, { name = "botocore" }, { name = "certifi" }, - { name = "cffi" }, { name = "charset-normalizer" }, { name = "cryptography" }, { name = "filelock" }, @@ -7497,41 +7524,41 @@ dependencies = [ { name = "tomlkit" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/df/41fe26b68801e3d59653a5dc7ce87a92e9d967dcad7b59b035b8c9804815/snowflake_connector_python-3.18.0.tar.gz", hash = "sha256:41a46eb9824574c5f8068e3ed5c02a2dc0a733ed08ee81fa1fb3dd0ebe921728", size = 798019, upload-time = "2025-10-06T12:15:34.301Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/f1/4aff125021a9c5e0183f2f55dd7d04b7256a0e1e10db50d537a7415d9c55/snowflake_connector_python-4.0.0.tar.gz", hash = "sha256:4b10a865c4a5e1fa60c365c7fe41e0433605e6e5edc824e8730a9038f330b3a6", size = 813937, upload-time = "2025-10-09T10:11:34.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/66/2be9bfebaad12f8b0fbeee68542f14b7a37801b991e3f99adab98ca235ff/snowflake_connector_python-3.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e17a9e806823d3a0e578cf9349f6a93810a582b3132903ea9e1683854d08da00", size = 1014396, upload-time = "2025-10-06T12:15:36.069Z" }, - { url = "https://files.pythonhosted.org/packages/8e/38/e00f81687b56a9419c2b0de3adcab449fc1e7d7a5383c29835148b1bb311/snowflake_connector_python-3.18.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:1841b60dc376639493dfc520cf39ad4f4da1f30286bba57e878d57414263d628", size = 1027175, upload-time = "2025-10-06T12:15:37.632Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ae/f45696a00e63fad3e153c01b8fe5c2d55aba954bb69bd09c7d2d0a290cba/snowflake_connector_python-3.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65d37263dd288abb649820b7e34af96dc6b2d2115bf5521a2526245f81ddb0cb", size = 2650237, upload-time = "2025-10-06T12:15:14.24Z" }, - { url = "https://files.pythonhosted.org/packages/c1/dd/843ac8067efb061f66c4e186c293270b887103b162a73559660b4fb0d31e/snowflake_connector_python-3.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fb9fc9d8c2c7d209ba89282d367a32e75b0688afd4a3f02409e24f153c1a32e", size = 2678195, upload-time = "2025-10-06T12:15:16.975Z" }, - { url = "https://files.pythonhosted.org/packages/e8/b2/035e0e316f875f4410d880e12a2765063c054e12e0184a3d86f2728818e5/snowflake_connector_python-3.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfa6b234f53ec624149e21156d0a98e43408d194f2e65bcfaf30acefd35a581e", size = 1161494, upload-time = "2025-10-06T12:15:51.363Z" }, - { url = "https://files.pythonhosted.org/packages/87/7e/b932b9897ea7e83b2184443c5222af2f71526e8bce91ecd64ac20b87527c/snowflake_connector_python-3.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5fcb9a25a9b77b6cd86dfc6a6324b9910e15a493a916983229011ce3509b5f", size = 1014589, upload-time = "2025-10-06T12:15:39.26Z" }, - { url = "https://files.pythonhosted.org/packages/7e/79/97f777d3d26392901910e27f0d41e9a6dc72fba40cb2499febfca7e51e81/snowflake_connector_python-3.18.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d89f608fde2fb0597ca5e020c4ac602027dc67f11b61b4d1e5448163bae4edc", size = 1027163, upload-time = "2025-10-06T12:15:40.651Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9f/553f9a2ec3ce4ab960c8d3611ecbd2e66f972841ef1e037dcbcd5148abae/snowflake_connector_python-3.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1afbd9e21180d2b4a76500ac2978b11865fdb3230609f2a9d80ba459fc27f2e4", size = 2661951, upload-time = "2025-10-06T12:15:18.676Z" }, - { url = "https://files.pythonhosted.org/packages/8a/bb/8213c682ea69cf50ba028db47469455cb7dba31b152b867ac3a468dcca19/snowflake_connector_python-3.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c068c8d3cd0c9736cb0679a9f544d34327e64415303bbfe07ec8ce3c5dae800", size = 2692086, upload-time = "2025-10-06T12:15:21.5Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6f/e651de2061f88e30cd271a023cc79e2e2683ff6aa2cb1e1045b8ba62d365/snowflake_connector_python-3.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:b211b4240596a225b895261a4ced2633e0262e82e2e32f6fb8dfc7d4bfedf8ca", size = 1161558, upload-time = "2025-10-06T12:15:53.091Z" }, - { url = "https://files.pythonhosted.org/packages/da/67/0df7829f295988c121f385c562d60c7a4989bc8f72885d04669ce5cd6516/snowflake_connector_python-3.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fee7035f865088f948510b094101c8a0e5b22501891f2115f7fb1cb555de76a", size = 1013717, upload-time = "2025-10-06T12:15:41.906Z" }, - { url = "https://files.pythonhosted.org/packages/4d/90/35353d5311735ebe85f0224f3a6e4f136c29e1b3e4ce6c7466c9b7e7931b/snowflake_connector_python-3.18.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:283366b35df88cd0c71caf0215ba80370ddef4dd37d2adf43b24208c747231ee", size = 1025471, upload-time = "2025-10-06T12:15:43.073Z" }, - { url = "https://files.pythonhosted.org/packages/ec/16/d490c00546ca8842d314de689ac718c73c9fe0f9b042e06703449282de7c/snowflake_connector_python-3.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e4c285cc6a7f6431cff98c8f235a0fe9da2262462dd3dfc2b97120574a95cf9", size = 2684000, upload-time = "2025-10-06T12:15:23.411Z" }, - { url = "https://files.pythonhosted.org/packages/d3/cb/4bc697af4138e17cccde506f28233492a6e1919ced7a65aa31b6f1e8bb6c/snowflake_connector_python-3.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94e041e347b5151b66d19d6cfc3b3172dac1f51e44bbf7cf58f3989427dd464a", size = 2715472, upload-time = "2025-10-06T12:15:25.062Z" }, - { url = "https://files.pythonhosted.org/packages/d9/72/815a4b9795ddce224a1392849dd34a408f2dac240bcdcb0539d42cfd31b1/snowflake_connector_python-3.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:7116cfa410d517328fd25fabffb54845b88667586718578c4333ce034fead1ba", size = 1160435, upload-time = "2025-10-06T12:15:55.046Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e6/b75caca8bcfeae1bc999bf70c9cb54a73607f361a3f1ef0b679e2bd850a6/snowflake_connector_python-3.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ed2d593f1983939d5d8d88b212d86fd4f14f0ceefc1df9882b4a18534adbde9", size = 1014849, upload-time = "2025-10-06T12:15:44.228Z" }, - { url = "https://files.pythonhosted.org/packages/4b/03/0420ebed3b9326e738ab06f8d3f80d9d430054e181ddfe3bf908d87ea5f9/snowflake_connector_python-3.18.0-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:b99f261c82be92224ac20c8c12bdf26ce3ed5dfd8a3df8a97f15a1e11c46ad27", size = 1026296, upload-time = "2025-10-06T12:15:46.82Z" }, - { url = "https://files.pythonhosted.org/packages/d5/04/a467a3bc6d59fd77b7628086a32102711cfb337b0920c3dac340a29f27e8/snowflake_connector_python-3.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51eb789a09dc6c62119cfabd044fba1a6b8378206f05a1e83ddb2e9cb49acc0b", size = 2685839, upload-time = "2025-10-06T12:15:26.475Z" }, - { url = "https://files.pythonhosted.org/packages/29/70/0ae9d661d405720b7e3bcea425f1915475b457e4a17fec4eb28b8bd91d35/snowflake_connector_python-3.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd1de3038b6d7059ca59f93e105aba2a673151c693cc4292f72f38bfaf147df2", size = 2718059, upload-time = "2025-10-06T12:15:27.765Z" }, - { url = "https://files.pythonhosted.org/packages/9d/38/ea46bbe910bd44ce52aaeea2fefe072392c7c6f3c04bfd0aea3f8fdd5e3a/snowflake_connector_python-3.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:aeeb181a156333480f60b5f8ddbb3d087e288b4509adbef7993236defe4d7570", size = 1160453, upload-time = "2025-10-06T12:15:58.405Z" }, + { url = "https://files.pythonhosted.org/packages/e4/75/f845ca5079a6b911023fa945dbf1bac0ed1c2f5967108b14440c740cb410/snowflake_connector_python-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2c3e0f6d103fe67c975550ed424f579d3e7ae503d56467e5549f3a1a1e0e8f24", size = 1030251, upload-time = "2025-10-09T10:11:36.37Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/3a7e36a9e53beeb27c0599d2703f33bb812be931b469b154b08df0eeeaf5/snowflake_connector_python-4.0.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:e8d5b66f283967c700fff2303ac5e52d1a3cf41990a634f121ac8b1f1cd9af10", size = 1043041, upload-time = "2025-10-09T10:11:37.719Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3b/bda95c4de593743c021a9968d70087674189c60a8317185de1b0f32d17c8/snowflake_connector_python-4.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad5d0f1ebcb2c6b7a7859ee3d4e02203087e40faae539a336bbcb45a3660777", size = 2666209, upload-time = "2025-10-09T10:11:17.54Z" }, + { url = "https://files.pythonhosted.org/packages/60/e6/30c4015e2712bf8bf83b54ddadeee0494b68ae6d0f6d49d9373f463305d4/snowflake_connector_python-4.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4106a66e770e564b3037457b7b01b15ca28aee61afb88560b664aa8af439b533", size = 2693962, upload-time = "2025-10-09T10:11:20.735Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e0/5f494b3353216e629f8b9d7269024518e0db9f2992df471de6ef43b60f7b/snowflake_connector_python-4.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:7789df78f7c7abfb351f2709258d05a94652cfe3c2c617fb15f15a11fc1b7b25", size = 1177353, upload-time = "2025-10-09T10:11:51.07Z" }, + { url = "https://files.pythonhosted.org/packages/0c/86/0dceb37f50cd28ee61af1f0396eccd17a563d56d66067e0842ff8dfafe6d/snowflake_connector_python-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ca2503f705627f7e045da6254d97c37210a3b0a18b43d0f1b29616d0c7aaa01", size = 1030446, upload-time = "2025-10-09T10:11:39.321Z" }, + { url = "https://files.pythonhosted.org/packages/7b/80/1ac8ae9494b2ee5bcc0e4cefe5aada86d0e61d21208a0107e99c5bec92ec/snowflake_connector_python-4.0.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:fd0d2d2c5cfd15f041e8522f5f8bdad0be4de7d805dd1646377fccd6bd404fa8", size = 1043031, upload-time = "2025-10-09T10:11:40.638Z" }, + { url = "https://files.pythonhosted.org/packages/fe/db/fbd1dbe2d6ca2b8f99337e39d596a800e6c54451a93a9167927a15f60534/snowflake_connector_python-4.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbdeec0d65c2e3f648c8b05839001c062984959417902717f7fc6eed983211d", size = 2677427, upload-time = "2025-10-09T10:11:22.537Z" }, + { url = "https://files.pythonhosted.org/packages/12/2b/ecb82ae3e07c65a0d137c9211570db63885843ec2a40eb71e168cc834bc4/snowflake_connector_python-4.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e376bad497c7932448cc29058e75737f02b3f0e25569de9e4ff0616944b4ceba", size = 2707806, upload-time = "2025-10-09T10:11:23.895Z" }, + { url = "https://files.pythonhosted.org/packages/76/f6/99a373042dcd4a83459ce915b1cdd22c3ac54bd8c2a727bb299cec85e0b2/snowflake_connector_python-4.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:af89a9e1355ea4dac7927d2f9bc15b2c81e381ad8bcdf8954ba3dd457a4d51d6", size = 1177415, upload-time = "2025-10-09T10:11:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b0/462c0deee35d6d03d3d729b3f923615bae665beb7f9a94673a23a52080fe/snowflake_connector_python-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bfd3b8523d7adc830f99c5c4c635689ceca61700a05368d5bbb34c6811f2ec54", size = 1029568, upload-time = "2025-10-09T10:11:42.125Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4b/bb3ae3f07e7927c8f16c4c0f1283d3c721978d16e8bf4193fc8e41025c1e/snowflake_connector_python-4.0.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:835161dd46ef8f5fc9d2f135ca654c2f3fbdf57b035d3e1980506aa8eac671dc", size = 1041337, upload-time = "2025-10-09T10:11:43.692Z" }, + { url = "https://files.pythonhosted.org/packages/9c/75/4bfac89f10c6dbb75e97adf1e217737fc599ebf964031c9298b6cbd807d0/snowflake_connector_python-4.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65e4e36dd1b0c7235d84cddef8a3c97c5ea0dc8fea85e31e45fc485000b77a83", size = 2699730, upload-time = "2025-10-09T10:11:25.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/78/0e916416c50909dbae511fe38b1e671a9efa62decdce51b174a0396804e4/snowflake_connector_python-4.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6132986d6965e4005b0167270612fbc7fa4bc4ef42726a40b85a8f57475a78d", size = 2731336, upload-time = "2025-10-09T10:11:27.028Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/3db8a2f3f5ee724d309c661af739a70d0643070b9b4597728151ef900f9b/snowflake_connector_python-4.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a790f06808e4481c23cfed1396d2c9a786060ddd62408b1fda1a63e1e6bc4b07", size = 1176292, upload-time = "2025-10-09T10:11:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/64/c0/10dfcce18514d711bf17d7766d24aedfc20d7a5aa0e8311c0d3068baf266/snowflake_connector_python-4.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4e8c3d2ea4055dd4aecc93514030341e300f557f2e86ca21eb47568c461a6f56", size = 1030702, upload-time = "2025-10-09T10:11:45.013Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/9d068375ccb341975eb95a87a99176b4b25bb7725e61c8ed62681f2d5123/snowflake_connector_python-4.0.0-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:1fea301e3d1e8022b9f2ff87dc3be139d5ed7be5e85fab8a6c59d400a02e6d58", size = 1042153, upload-time = "2025-10-09T10:11:46.309Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ae/f4da6b62e546f48885b63bc1884c935bc293e6da9605ddcd217e21307a63/snowflake_connector_python-4.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e648bbd506a0f2f8076f9eafe231b2d4284b1a884528c3a0690391ab2bb54e", size = 2701637, upload-time = "2025-10-09T10:11:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/88/bf/6cf92dbd1c6d95311894404e2c46db9a06ff6d37bea9a19e667d0bf26362/snowflake_connector_python-4.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f67d844241a6fed764a8f04d32c0273aedf9159d5162b764748526277c7f8831", size = 2733899, upload-time = "2025-10-09T10:11:30.186Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c8/7d9a41e1b10c0a2bae86241773a6b55c06e897c74b3cab14ec8315e16b34/snowflake_connector_python-4.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:cd23bff2abc74e34c6123a181c004ead9e6cc8ef2661250892afd64bad24533c", size = 1176311, upload-time = "2025-10-09T10:11:56.176Z" }, ] [[package]] name = "snowflake-sqlalchemy" -version = "1.7.7" +version = "1.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "snowflake-connector-python" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/35/99c9d8ae12fd3799a46f3ebf86d4a4d7e0816f8c738f4545f2909b6b8756/snowflake_sqlalchemy-1.7.7.tar.gz", hash = "sha256:4ae5e5b458596ab2f0380c79b049978681a0490791add478d3c953613417d086", size = 121207, upload-time = "2025-09-09T14:37:42.978Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/a5/540b4a291465903f328bb59c86a80bd825c3bfde8e270a1970c37a181a3d/snowflake_sqlalchemy-1.8.0.tar.gz", hash = "sha256:50f452850a968ca3358fda6c53b5c29203c16013afdb02ab90d79b53cf02407f", size = 122538, upload-time = "2025-12-04T20:13:23.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/86/18210ab4a07e1b22494cc6738a4606f66afe75567090006ecd372f631f00/snowflake_sqlalchemy-1.7.7-py3-none-any.whl", hash = "sha256:e6cf9f6309a9c3f4b3fd6e8808b2fb04886da123f4d58d96323a491732a5d496", size = 72399, upload-time = "2025-09-09T14:37:41.79Z" }, + { url = "https://files.pythonhosted.org/packages/86/91/61261b9e4d9af757ebab08fb50fc4e4045b3e304c812537a3173eff5bd38/snowflake_sqlalchemy-1.8.0-py3-none-any.whl", hash = "sha256:067d2f1509f8d57938d07b0d441ae3aa19ed07acf54d2aab7c29ebf5c1426cf5", size = 72728, upload-time = "2025-12-04T20:13:20.432Z" }, ] [[package]] @@ -7569,47 +7596,41 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.45" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912, upload-time = "2025-12-09T21:05:16.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a7/e9ccfa7eecaf34c6f57d8cb0bb7cbdeeff27017cc0f5d0ca90fdde7a7c0d/sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce", size = 2137282, upload-time = "2025-10-10T15:36:10.965Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e1/50bc121885bdf10833a4f65ecbe9fe229a3215f4d65a58da8a181734cae3/sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985", size = 2127322, upload-time = "2025-10-10T15:36:12.428Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/a8573b7230a3ce5ee4b961a2d510d71b43872513647398e595b744344664/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0", size = 3214772, upload-time = "2025-10-10T15:34:15.09Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/c63d8adb6a7edaf8dcb6f75a2b1e9f8577960a1e489606859c4d73e7d32b/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e", size = 3214434, upload-time = "2025-10-10T15:47:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/ee/a6/243d277a4b54fae74d4797957a7320a5c210c293487f931cbe036debb697/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749", size = 3155365, upload-time = "2025-10-10T15:34:17.932Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f8/6a39516ddd75429fd4ee5a0d72e4c80639fab329b2467c75f363c2ed9751/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2", size = 3178910, upload-time = "2025-10-10T15:47:02.346Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624, upload-time = "2025-10-10T15:38:15.552Z" }, - { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240, upload-time = "2025-10-10T15:38:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e3/81/15d7c161c9ddf0900b076b55345872ed04ff1ed6a0666e5e94ab44b0163c/sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd", size = 2140517, upload-time = "2025-10-10T15:36:15.64Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d5/4abd13b245c7d91bdf131d4916fd9e96a584dac74215f8b5bc945206a974/sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa", size = 2130738, upload-time = "2025-10-10T15:36:16.91Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3c/8418969879c26522019c1025171cefbb2a8586b6789ea13254ac602986c0/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e", size = 3304145, upload-time = "2025-10-10T15:34:19.569Z" }, - { url = "https://files.pythonhosted.org/packages/94/2d/fdb9246d9d32518bda5d90f4b65030b9bf403a935cfe4c36a474846517cb/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e", size = 3304511, upload-time = "2025-10-10T15:47:05.088Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/40f2ad1da97d5c83f6c1269664678293d3fe28e90ad17a1093b735420549/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399", size = 3235161, upload-time = "2025-10-10T15:34:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/95/cb/7cf4078b46752dca917d18cf31910d4eff6076e5b513c2d66100c4293d83/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b", size = 3261426, upload-time = "2025-10-10T15:47:07.196Z" }, - { url = "https://files.pythonhosted.org/packages/f8/3b/55c09b285cb2d55bdfa711e778bdffdd0dc3ffa052b0af41f1c5d6e582fa/sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3", size = 2105392, upload-time = "2025-10-10T15:38:20.051Z" }, - { url = "https://files.pythonhosted.org/packages/c7/23/907193c2f4d680aedbfbdf7bf24c13925e3c7c292e813326c1b84a0b878e/sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5", size = 2130293, upload-time = "2025-10-10T15:38:21.601Z" }, - { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675, upload-time = "2025-10-10T16:03:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726, upload-time = "2025-10-10T16:03:35.934Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603, upload-time = "2025-10-10T15:35:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842, upload-time = "2025-10-10T15:43:45.431Z" }, - { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558, upload-time = "2025-10-10T15:35:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570, upload-time = "2025-10-10T15:43:48.407Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447, upload-time = "2025-10-10T15:03:21.678Z" }, - { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912, upload-time = "2025-10-10T15:03:24.656Z" }, - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051, upload-time = "2025-12-09T22:06:04.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781, upload-time = "2025-12-09T22:09:54.435Z" }, + { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096, upload-time = "2025-12-09T22:06:06.169Z" }, + { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109, upload-time = "2025-12-09T22:09:55.969Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240, upload-time = "2025-12-09T21:29:54.007Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615, upload-time = "2025-12-09T21:29:55.85Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f8/9be54ff620e5b796ca7b44670ef58bc678095d51b0e89d6e3102ea468216/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b", size = 3309379, upload-time = "2025-12-09T22:06:07.461Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2b/60ce3ee7a5ae172bfcd419ce23259bb874d2cddd44f67c5df3760a1e22f9/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac", size = 3309948, upload-time = "2025-12-09T22:09:57.643Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/bac8d393f5db550e4e466d03d16daaafd2bad1f74e48c12673fb499a7fc1/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606", size = 3261239, upload-time = "2025-12-09T22:06:08.879Z" }, + { url = "https://files.pythonhosted.org/packages/6f/12/43dc70a0528c59842b04ea1c1ed176f072a9b383190eb015384dd102fb19/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c", size = 3284065, upload-time = "2025-12-09T22:09:59.454Z" }, + { url = "https://files.pythonhosted.org/packages/cf/9c/563049cf761d9a2ec7bc489f7879e9d94e7b590496bea5bbee9ed7b4cc32/sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177", size = 2113480, upload-time = "2025-12-09T21:29:57.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fa/09d0a11fe9f15c7fa5c7f0dd26be3d235b0c0cbf2f9544f43bc42efc8a24/sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2", size = 2138407, upload-time = "2025-12-09T21:29:58.556Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c7/1900b56ce19bff1c26f39a4ce427faec7716c81ac792bfac8b6a9f3dca93/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f", size = 3333760, upload-time = "2025-12-09T22:11:02.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/93/3be94d96bb442d0d9a60e55a6bb6e0958dd3457751c6f8502e56ef95fed0/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d", size = 3348268, upload-time = "2025-12-09T22:13:49.054Z" }, + { url = "https://files.pythonhosted.org/packages/48/4b/f88ded696e61513595e4a9778f9d3f2bf7332cce4eb0c7cedaabddd6687b/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4", size = 3278144, upload-time = "2025-12-09T22:11:04.14Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/310ecb5657221f3e1bd5288ed83aa554923fb5da48d760a9f7622afeb065/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6", size = 3313907, upload-time = "2025-12-09T22:13:50.598Z" }, + { url = "https://files.pythonhosted.org/packages/5c/39/69c0b4051079addd57c84a5bfb34920d87456dd4c90cf7ee0df6efafc8ff/sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953", size = 2112182, upload-time = "2025-12-09T21:39:30.824Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4e/510db49dd89fc3a6e994bee51848c94c48c4a00dc905e8d0133c251f41a7/sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1", size = 2139200, upload-time = "2025-12-09T21:39:32.321Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c8/7cc5221b47a54edc72a0140a1efa56e0a2730eefa4058d7ed0b4c4357ff8/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf", size = 3277082, upload-time = "2025-12-09T22:11:06.167Z" }, + { url = "https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e", size = 3293131, upload-time = "2025-12-09T22:13:52.626Z" }, + { url = "https://files.pythonhosted.org/packages/da/4c/13dab31266fc9904f7609a5dc308a2432a066141d65b857760c3bef97e69/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b", size = 3225389, upload-time = "2025-12-09T22:11:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/74/04/891b5c2e9f83589de202e7abaf24cd4e4fa59e1837d64d528829ad6cc107/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8", size = 3266054, upload-time = "2025-12-09T22:13:54.262Z" }, + { url = "https://files.pythonhosted.org/packages/f1/24/fc59e7f71b0948cdd4cff7a286210e86b0443ef1d18a23b0d83b87e4b1f7/sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a", size = 2110299, upload-time = "2025-12-09T21:39:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee", size = 2136264, upload-time = "2025-12-09T21:39:36.801Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8d/bb40a5d10e7a5f2195f235c0b2f2c79b0bf6e8f00c0c223130a4fbd2db09/sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6", size = 3521998, upload-time = "2025-12-09T22:13:28.622Z" }, + { url = "https://files.pythonhosted.org/packages/75/a5/346128b0464886f036c039ea287b7332a410aa2d3fb0bb5d404cb8861635/sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a", size = 3473434, upload-time = "2025-12-09T22:13:30.188Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, ] [[package]] @@ -7700,16 +7721,16 @@ wheels = [ [[package]] name = "tavily-python" -version = "0.7.13" +version = "0.7.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "requests" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/fb/b6d6327a78b9107681701d56b3a2dda48dc5420a6ee8b33a147d87bfba60/tavily_python-0.7.13.tar.gz", hash = "sha256:347f92402331d071557f6dd6680f813a7d484b4ba7240905cc397cd192d1355c", size = 17237, upload-time = "2025-11-13T18:53:03.998Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/cb/c9df2f11b06a2336d449ba48b45f013133d8da7af7b0d9414d2055796734/tavily_python-0.7.14.tar.gz", hash = "sha256:3d7421fcd12609a7d8afe1d0ba27bc968cd44aa916667f7e8b46f8cdf9c6f784", size = 21023, upload-time = "2025-12-04T23:27:00.739Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/4d/e5e4c65cd66144ac3d0d5a6a2bbfba22eb6a63e6e450beba10ee8413b86d/tavily_python-0.7.13-py3-none-any.whl", hash = "sha256:911825467f2bb19b8162b4766d3e81081160a7c0fb8a15c7c716b2bef73e6296", size = 15484, upload-time = "2025-11-13T18:53:02.821Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1b/d3735f8af5a9be370e8ecf660eca31230c20018fa7216eb3568e9761be0e/tavily_python-0.7.14-py3-none-any.whl", hash = "sha256:496d40785d620d820aaa6dc449fdcacebb05f1410a17b278891e717ac97e1ec9", size = 17950, upload-time = "2025-12-04T23:26:59.465Z" }, ] [[package]] @@ -7896,7 +7917,7 @@ dependencies = [ { name = "fsspec" }, { name = "jinja2" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -8279,7 +8300,7 @@ resolution-markers = [ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.11' and platform_python_implementation != 'PyPy' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b4/40/66afbb030f4a800c08a9312a0653a7aec06ce0bd633d83215eb0f83c0f46/types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1", size = 17134, upload-time = "2024-04-06T02:13:39.267Z" } wheels = [ @@ -8288,11 +8309,11 @@ wheels = [ [[package]] name = "types-s3transfer" -version = "0.15.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/bf/b00dcbecb037c4999b83c8109b8096fe78f87f1266cadc4f95d4af196292/types_s3transfer-0.15.0.tar.gz", hash = "sha256:43a523e0c43a88e447dfda5f4f6b63bf3da85316fdd2625f650817f2b170b5f7", size = 14236, upload-time = "2025-11-21T21:16:26.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/64/42689150509eb3e6e82b33ee3d89045de1592488842ddf23c56957786d05/types_s3transfer-0.16.0.tar.gz", hash = "sha256:b4636472024c5e2b62278c5b759661efeb52a81851cde5f092f24100b1ecb443", size = 13557, upload-time = "2025-12-08T08:13:09.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/39/39a322d7209cc259e3e27c4d498129e9583a2f3a8aea57eb1a9941cb5e9e/types_s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:1e617b14a9d3ce5be565f4b187fafa1d96075546b52072121f8fda8e0a444aed", size = 19702, upload-time = "2025-11-21T21:16:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/27/e88220fe6274eccd3bdf95d9382918716d312f6f6cef6a46332d1ee2feff/types_s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:1c0cd111ecf6e21437cb410f5cddb631bfb2263b77ad973e79b9c6d0cb24e0ef", size = 19247, upload-time = "2025-12-08T08:13:08.426Z" }, ] [[package]] @@ -8387,7 +8408,7 @@ all-docs = [ { name = "markdown" }, { name = "msoffcrypto-tool" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "onnx" }, { name = "onnxruntime" }, { name = "openpyxl" }, @@ -8410,7 +8431,7 @@ local-inference = [ { name = "markdown" }, { name = "msoffcrypto-tool" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "onnx" }, { name = "onnxruntime" }, { name = "openpyxl" }, @@ -8518,7 +8539,7 @@ socks = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin'", @@ -8534,9 +8555,9 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 'aarch64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux'", "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.11' and platform_python_implementation != 'PyPy' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" }, ] [package.optional-dependencies] @@ -8575,28 +8596,28 @@ wheels = [ [[package]] name = "uv" -version = "0.9.15" +version = "0.9.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/b8/63e4ad24d7ef24ef1de10cb2db9ff0f74b2cceb4bd71c4b3909297d40967/uv-0.9.15.tar.gz", hash = "sha256:241a57d8ce90273d0ad8460897e1b2250bd4aa6bafe72fd8be07fbc3a7662f3d", size = 3788825, upload-time = "2025-12-03T01:33:06.404Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/1a/cb0c37ae8513b253bcbc13d42392feb7d95ea696eb398b37535a28df9040/uv-0.9.17.tar.gz", hash = "sha256:6d93ab9012673e82039cfa7f9f66f69b388bc3f910f9e8a2ebee211353f620aa", size = 3815957, upload-time = "2025-12-09T23:01:21.756Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/e7/5c7fcf7a49884273c18a54b68ffd5f05775cb0e59aeeb2801f2b6d31787b/uv-0.9.15-py3-none-linux_armv6l.whl", hash = "sha256:4ccf2aa7f2e0fcb553dccc8badceb2fc533000e5baf144fd982bb9be88b304b8", size = 20936584, upload-time = "2025-12-03T01:32:58.983Z" }, - { url = "https://files.pythonhosted.org/packages/30/a8/5c376318f57dd4fc58bb01db870f9e564a689d479ddc0440731933286740/uv-0.9.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:944cd6d500974f9693994ec990c5b263a185e66cbc1cbd230f319445f8330e4e", size = 20000398, upload-time = "2025-12-03T01:33:04.518Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c7/6af7e5dc21902eb936a54c037650f070cea015d742b3a49e82f53e6f32c4/uv-0.9.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ec4716fee5db65cfc78016d07f6fddb9b1fa29a0471fe67fe6676e2befee3215", size = 18440619, upload-time = "2025-12-03T01:32:56.388Z" }, - { url = "https://files.pythonhosted.org/packages/88/73/8364801f678ba58d1a31ec9c8e0bfc407b48c0c57e0e3618e8fbf6b285f6/uv-0.9.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:2b9ad2581a90b7b2ed3f5a3b2657c3cf84119bdf101e1a1c49a2f38577eecb1b", size = 20326432, upload-time = "2025-12-03T01:32:53.805Z" }, - { url = "https://files.pythonhosted.org/packages/89/ed/9c13d81005b00dcc759f7ea4b640b1efff8ecebbf852df90c2f237373ed5/uv-0.9.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d6a837537294e283ddbb18c494dbc085afca3e29d2176059140b7b4e94e485fd", size = 20531552, upload-time = "2025-12-03T01:33:18.894Z" }, - { url = "https://files.pythonhosted.org/packages/b1/24/99c26056300c83f0d542272c02465937965191739d44bf8654d09d2d296f/uv-0.9.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:214013314564347ad629a5cfcd28b962038fc23c72155d7a798313f6b9f64f81", size = 21428020, upload-time = "2025-12-03T01:33:10.373Z" }, - { url = "https://files.pythonhosted.org/packages/a2/4a/15dd32d695eae71cb4c09af6e9cbde703674824653c9f2963b068108b344/uv-0.9.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b32c1ac11ea80ab4cc6ea61029880e1319041d2b3783f2478c8eadfc9a9c9d5a", size = 23061719, upload-time = "2025-12-03T01:32:48.914Z" }, - { url = "https://files.pythonhosted.org/packages/51/02/83c179d6a5cfee0c437dd1d73b515557c6b2b7ab19fb9421420c13b10bc8/uv-0.9.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d99dfe8af75fed7fd6b7f6a8a81ba26ac88e046c5cb366184c2b53b023b070", size = 22609336, upload-time = "2025-12-03T01:32:41.645Z" }, - { url = "https://files.pythonhosted.org/packages/a3/77/c620febe2662ab1897c2ef7c95a5424517efc456b77a1f75f6da81b4e542/uv-0.9.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5375c95a70fc76104fc178771cd4a8db1dab55345c7162c3d2d47ca2993c4bb", size = 21663700, upload-time = "2025-12-03T01:32:36.335Z" }, - { url = "https://files.pythonhosted.org/packages/70/1b/4273d02565a4e86f238e9fee23e6c5c3bb7b5c237c63228e1b72451db224/uv-0.9.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9c1cefdd6e878baa32ff7a7d1ef463690750ff45d899fbb6bd6705e8329b00a", size = 21714799, upload-time = "2025-12-03T01:33:01.785Z" }, - { url = "https://files.pythonhosted.org/packages/61/ae/29787af8124d821c1a88bb66612c24ff6180309d35348a4915214c5078d3/uv-0.9.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b6d342a4610e7dbc3450c8000313ee7693905ddee12a65a34fdbcd8418a852a5", size = 20454218, upload-time = "2025-12-03T01:33:14.113Z" }, - { url = "https://files.pythonhosted.org/packages/56/fe/ab906a1e530f0fbb7042e82057d7c08ef46131deb75d5fef2133de6725c5/uv-0.9.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dea4fd678d48b9790234de43b2e5e4a445f9541fd361db7dc1a2cac861c9717a", size = 21549370, upload-time = "2025-12-03T01:32:46.766Z" }, - { url = "https://files.pythonhosted.org/packages/19/11/20ea6dc5ca56f2ad9a8f1b5674f84c38a45a28ccf880aa7c1abb226355a6/uv-0.9.15-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:bae65f4748cacc20ea7f93b94a9ba82dd5505a98cf61e33d75e2669c6aefbfc5", size = 20502715, upload-time = "2025-12-03T01:32:51.454Z" }, - { url = "https://files.pythonhosted.org/packages/37/38/ed98b5f48be2aeb63dc8853bc777b739c18277d0b950196b4d46f7b4d74a/uv-0.9.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:5473c1095a697b7c7996a312232f78e81cf71e5afc30c889e46a486fe298d853", size = 20890379, upload-time = "2025-12-03T01:32:28.035Z" }, - { url = "https://files.pythonhosted.org/packages/dd/50/a97468ed93b80a50ea97a717662be4b841e7149bc68197ded087bcdd9e36/uv-0.9.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a0ef2e2bcf807aebd70e3a87bde618d0a01f07f627f5da0b0b457d7be2124843", size = 21921653, upload-time = "2025-12-03T01:32:31.016Z" }, - { url = "https://files.pythonhosted.org/packages/94/e8/ad85878d4e789c40b75c9f670eba52c7e5e63393f31e1c1a7246849595a2/uv-0.9.15-py3-none-win32.whl", hash = "sha256:5efa39d9085f918d17869e43471ccd4526372e71d945b8d7cd3c8867ce6eab33", size = 19762699, upload-time = "2025-12-03T01:32:39.104Z" }, - { url = "https://files.pythonhosted.org/packages/bc/37/e15a46f880b2c230f7d6934f046825ebe547f90008984c861e84e2ef34c4/uv-0.9.15-py3-none-win_amd64.whl", hash = "sha256:e46f36c80353fb406d4c0fb2cfe1953b4a01bfb3fc5b88dd4f763641b8899f1a", size = 21758441, upload-time = "2025-12-03T01:32:33.722Z" }, - { url = "https://files.pythonhosted.org/packages/9a/fe/af3595c25dfaa85daff11ed0c8ca0fc1d088a426c65ef93d3751490673fd/uv-0.9.15-py3-none-win_arm64.whl", hash = "sha256:8f14456f357ebbf3f494ae5af41e9b306ba66ecc81cb2304095b38d99a1f7d28", size = 20203340, upload-time = "2025-12-03T01:32:44.184Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e2/b6e2d473bdc37f4d86307151b53c0776e9925de7376ce297e92eab2e8894/uv-0.9.17-py3-none-linux_armv6l.whl", hash = "sha256:c708e6560ae5bc3cda1ba93f0094148ce773b6764240ced433acf88879e57a67", size = 21254511, upload-time = "2025-12-09T23:00:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/d5/40/75f1529a8bf33cc5c885048e64a014c3096db5ac7826c71e20f2b731b588/uv-0.9.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:233b3d90f104c59d602abf434898057876b87f64df67a37129877d6dab6e5e10", size = 20384366, upload-time = "2025-12-09T23:01:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/de/30/b3a343893681a569cbb74f8747a1c24e5f18ca9e07de0430aceaf9389ef4/uv-0.9.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4b8e5513d48a267bfa180ca7fefaf6f27b1267e191573b3dba059981143e88ef", size = 18924624, upload-time = "2025-12-09T23:01:10.291Z" }, + { url = "https://files.pythonhosted.org/packages/21/56/9daf8bbe4a9a36eb0b9257cf5e1e20f9433d0ce996778ccf1929cbe071a4/uv-0.9.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8f283488bbcf19754910cc1ae7349c567918d6367c596e5a75d4751e0080eee0", size = 20671687, upload-time = "2025-12-09T23:00:51.927Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c8/4050ff7dc692770092042fcef57223b8852662544f5981a7f6cac8fc488d/uv-0.9.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cf8052ba669dc17bdba75dae655094d820f4044990ea95c01ec9688c182f1da", size = 20861866, upload-time = "2025-12-09T23:01:12.555Z" }, + { url = "https://files.pythonhosted.org/packages/84/d4/208e62b7db7a65cb3390a11604c59937e387d07ed9f8b63b54edb55e2292/uv-0.9.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06749461b11175a884be193120044e7f632a55e2624d9203398808907d346aad", size = 21858420, upload-time = "2025-12-09T23:01:00.009Z" }, + { url = "https://files.pythonhosted.org/packages/86/2c/91288cd5a04db37dfc1e0dad26ead84787db5832d9836b4cc8e0fa7f3c53/uv-0.9.17-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:35eb1a519688209160e48e1bb8032d36d285948a13b4dd21afe7ec36dc2a9787", size = 23471658, upload-time = "2025-12-09T23:00:49.503Z" }, + { url = "https://files.pythonhosted.org/packages/44/ba/493eba650ffad1df9e04fd8eabfc2d0aebc23e8f378acaaee9d95ca43518/uv-0.9.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bfb60a533e82690ab17dfe619ff7f294d053415645800d38d13062170230714", size = 23062950, upload-time = "2025-12-09T23:00:39.055Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9e/f7f679503c06843ba59451e3193f35fb7c782ff0afc697020d4718a7de46/uv-0.9.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd0f3e380ff148aff3d769e95a9743cb29c7f040d7ef2896cafe8063279a6bc1", size = 22080299, upload-time = "2025-12-09T23:00:44.026Z" }, + { url = "https://files.pythonhosted.org/packages/32/2e/76ba33c7d9efe9f17480db1b94d3393025062005e346bb8b3660554526da/uv-0.9.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2c3d25fbd8f91b30d0fac69a13b8e2c2cd8e606d7e6e924c1423e4ff84e616", size = 22087554, upload-time = "2025-12-09T23:00:41.715Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/ef4aae4a6c49076db2acd2a7b0278ddf3dbf785d5172b3165018b96ba2fb/uv-0.9.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:330e7085857e4205c5196a417aca81cfbfa936a97dd2a0871f6560a88424ebf2", size = 20823225, upload-time = "2025-12-09T23:00:57.041Z" }, + { url = "https://files.pythonhosted.org/packages/11/73/e0f816cacd802a1cb25e71de9d60e57fa1f6c659eb5599cef708668618cc/uv-0.9.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:45880faa9f6cf91e3cda4e5f947da6a1004238fdc0ed4ebc18783a12ce197312", size = 22004893, upload-time = "2025-12-09T23:01:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/15/6b/700f6256ee191136eb06e40d16970a4fc687efdccf5e67c553a258063019/uv-0.9.17-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8e775a1b94c6f248e22f0ce2f86ed37c24e10ae31fb98b7e1b9f9a3189d25991", size = 20853850, upload-time = "2025-12-09T23:01:02.694Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6a/13f02e2ed6510223c40f74804586b09e5151d9319f93aab1e49d91db13bb/uv-0.9.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8650c894401ec96488a6fd84a5b4675e09be102f5525c902a12ba1c8ef8ff230", size = 21322623, upload-time = "2025-12-09T23:00:46.806Z" }, + { url = "https://files.pythonhosted.org/packages/d0/18/2d19780cebfbec877ea645463410c17859f8070f79c1a34568b153d78e1d/uv-0.9.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:673066b72d8b6c86be0dae6d5f73926bcee8e4810f1690d7b8ce5429d919cde3", size = 22290123, upload-time = "2025-12-09T23:00:54.394Z" }, + { url = "https://files.pythonhosted.org/packages/77/69/ab79bde3f7b6d2ac89f839ea40411a9cf3e67abede2278806305b6ba797e/uv-0.9.17-py3-none-win32.whl", hash = "sha256:7407d45afeae12399de048f7c8c2256546899c94bd7892dbddfae6766616f5a3", size = 20070709, upload-time = "2025-12-09T23:01:05.105Z" }, + { url = "https://files.pythonhosted.org/packages/08/a0/ab5b1850197bf407d095361b214352e40805441791fed35b891621cb1562/uv-0.9.17-py3-none-win_amd64.whl", hash = "sha256:22fcc26755abebdf366becc529b2872a831ce8bb14b36b6a80d443a1d7f84d3b", size = 22122852, upload-time = "2025-12-09T23:01:07.783Z" }, + { url = "https://files.pythonhosted.org/packages/37/ef/813cfedda3c8e49d8b59a41c14fcc652174facfd7a1caf9fee162b40ccbd/uv-0.9.17-py3-none-win_arm64.whl", hash = "sha256:6761076b27a763d0ede2f5e72455d2a46968ff334badf8312bb35988c5254831", size = 20435751, upload-time = "2025-12-09T23:01:19.732Z" }, ] [[package]] @@ -8672,7 +8693,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation == 'PyPy'" }, - { name = "urllib3", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, + { name = "urllib3", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_python_implementation != 'PyPy'" }, { name = "wrapt" }, { name = "yarl" }, ] @@ -8698,11 +8719,12 @@ wheels = [ [[package]] name = "voyageai" -version = "0.3.5" +version = "0.3.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "aiolimiter" }, + { name = "ffmpeg-python" }, { name = "langchain-text-splitters" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -8712,9 +8734,9 @@ dependencies = [ { name = "tenacity" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/9b/e40f90793c1d03610b6109852791f752fcb257989a96701258278f874e00/voyageai-0.3.5.tar.gz", hash = "sha256:963e0d71611af529fa0e496db232a4f660b5f73bce7af1ab288a7f59df7512da", size = 20414, upload-time = "2025-09-11T00:28:26.29Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/a6/5f93fcd9c8c1a05873d287f1600e04f6cbfb2d42fce15ed75b0d5bebb9fa/voyageai-0.3.6.tar.gz", hash = "sha256:411e7c11eae4917429f091553a9b6911860df626811fb9415f24197d0cc5e219", size = 26346, upload-time = "2025-12-09T01:32:52.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/9d/709f5c7fc80a7bf11952fbccfca2bc5525bd5d345521795358819bd01d02/voyageai-0.3.5-py3-none-any.whl", hash = "sha256:1f70fcf3532d7e0bbc4332b1831a6fc1f714f268eeddc8b2859b81bf06a82411", size = 28257, upload-time = "2025-09-11T00:28:24.62Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e9/e13785fb2a3c605ea924ce2e54d235e423f6d6e45ddb09574963655ec111/voyageai-0.3.6-py3-none-any.whl", hash = "sha256:e282f9cef87eb949e2dd30ffe911689f1068c50b8c3c6e90e97793f2a52c83dd", size = 34465, upload-time = "2025-12-09T01:32:51.32Z" }, ] [[package]] From 467ee2917ea477d83b74d198a44b4a6fa0ca0732 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Tue, 30 Dec 2025 11:36:31 -0800 Subject: [PATCH 2/3] =?UTF-8?q?Improve=20EventListener=20and=20TraceCollec?= =?UTF-8?q?tionListener=20for=20improved=20event=E2=80=A6=20(#4160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor EventListener and TraceCollectionListener for improved event handling - Removed unused threading and method branches from EventListener to simplify the code. - Updated event handling methods in EventListener to use new formatter methods for better clarity and consistency. - Refactored TraceCollectionListener to eliminate unnecessary parameters in formatter calls, enhancing readability. - Simplified ConsoleFormatter by removing outdated tree management methods and focusing on panel-based output for status updates. - Enhanced ToolUsage to track run attempts for better tool usage metrics. * clearer for knowledge retrieval and dropped some reduancies * Refactor EventListener and ConsoleFormatter for improved clarity and consistency - Removed the MCPToolExecutionCompletedEvent handler from EventListener to streamline event processing. - Updated ConsoleFormatter to enhance output formatting by adding line breaks for better readability in status content. - Renamed status messages for MCP Tool execution to provide clearer context during tool operations. * fix run attempt incrementation * task name consistency * memory events consistency * ensure hitl works * linting --- .../base_agent_executor_mixin.py | 65 +- .../src/crewai/agents/crew_agent_executor.py | 11 +- .../src/crewai/events/event_listener.py | 206 +- .../listeners/tracing/trace_listener.py | 29 +- .../crewai/events/utils/console_formatter.py | 2017 ++++------------- lib/crewai/src/crewai/tools/tool_usage.py | 2 + .../test_flow_human_input_integration.py | 171 +- .../test_console_formatter_pause_resume.py | 137 +- 8 files changed, 651 insertions(+), 1987 deletions(-) diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor_mixin.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor_mixin.py index 5864a4995..cc4c9d4d8 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor_mixin.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor_mixin.py @@ -3,6 +3,7 @@ from __future__ import annotations import time from typing import TYPE_CHECKING +from crewai.agents.parser import AgentFinish from crewai.events.event_listener import event_listener from crewai.memory.entity.entity_memory_item import EntityMemoryItem from crewai.memory.long_term.long_term_memory_item import LongTermMemoryItem @@ -29,7 +30,7 @@ class CrewAgentExecutorMixin: _i18n: I18N _printer: Printer = Printer() - def _create_short_term_memory(self, output) -> None: + def _create_short_term_memory(self, output: AgentFinish) -> None: """Create and save a short-term memory item if conditions are met.""" if ( self.crew @@ -53,7 +54,7 @@ class CrewAgentExecutorMixin: "error", f"Failed to add to short term memory: {e}" ) - def _create_external_memory(self, output) -> None: + def _create_external_memory(self, output: AgentFinish) -> None: """Create and save a external-term memory item if conditions are met.""" if ( self.crew @@ -75,7 +76,7 @@ class CrewAgentExecutorMixin: "error", f"Failed to add to external memory: {e}" ) - def _create_long_term_memory(self, output) -> None: + def _create_long_term_memory(self, output: AgentFinish) -> None: """Create and save long-term and entity memory items based on evaluation.""" if ( self.crew @@ -136,40 +137,50 @@ class CrewAgentExecutorMixin: ) def _ask_human_input(self, final_answer: str) -> str: - """Prompt human input with mode-appropriate messaging.""" - event_listener.formatter.pause_live_updates() - try: - self._printer.print( - content=f"\033[1m\033[95m ## Final Result:\033[00m \033[92m{final_answer}\033[00m" - ) + """Prompt human input with mode-appropriate messaging. + Note: The final answer is already displayed via the AgentLogsExecutionEvent + panel, so we only show the feedback prompt here. + """ + from rich.panel import Panel + from rich.text import Text + + formatter = event_listener.formatter + formatter.pause_live_updates() + + try: # Training mode prompt (single iteration) if self.crew and getattr(self.crew, "_train", False): - prompt = ( - "\n\n=====\n" - "## TRAINING MODE: Provide feedback to improve the agent's performance.\n" + prompt_text = ( + "TRAINING MODE: Provide feedback to improve the agent's performance.\n\n" "This will be used to train better versions of the agent.\n" - "Please provide detailed feedback about the result quality and reasoning process.\n" - "=====\n" + "Please provide detailed feedback about the result quality and reasoning process." ) + title = "🎓 Training Feedback Required" # Regular human-in-the-loop prompt (multiple iterations) else: - prompt = ( - "\n\n=====\n" - "## HUMAN FEEDBACK: Provide feedback on the Final Result and Agent's actions.\n" - "Please follow these guidelines:\n" - " - If you are happy with the result, simply hit Enter without typing anything.\n" - " - Otherwise, provide specific improvement requests.\n" - " - You can provide multiple rounds of feedback until satisfied.\n" - "=====\n" + prompt_text = ( + "Provide feedback on the Final Result above.\n\n" + "• If you are happy with the result, simply hit Enter without typing anything.\n" + "• Otherwise, provide specific improvement requests.\n" + "• You can provide multiple rounds of feedback until satisfied." ) + title = "💬 Human Feedback Required" + + content = Text() + content.append(prompt_text, style="yellow") + + prompt_panel = Panel( + content, + title=title, + border_style="yellow", + padding=(1, 2), + ) + formatter.console.print(prompt_panel) - self._printer.print(content=prompt, color="bold_yellow") response = input() if response.strip() != "": - self._printer.print( - content="\nProcessing your feedback...", color="cyan" - ) + formatter.console.print("\n[cyan]Processing your feedback...[/cyan]") return response finally: - event_listener.formatter.resume_live_updates() + formatter.resume_live_updates() diff --git a/lib/crewai/src/crewai/agents/crew_agent_executor.py b/lib/crewai/src/crewai/agents/crew_agent_executor.py index e11e3e80a..45d4f84f3 100644 --- a/lib/crewai/src/crewai/agents/crew_agent_executor.py +++ b/lib/crewai/src/crewai/agents/crew_agent_executor.py @@ -7,6 +7,7 @@ and memory management. from __future__ import annotations from collections.abc import Callable +import logging from typing import TYPE_CHECKING, Any, Literal, cast from pydantic import BaseModel, GetCoreSchemaHandler @@ -51,6 +52,8 @@ from crewai.utilities.tool_utils import ( from crewai.utilities.training_handler import CrewTrainingHandler +logger = logging.getLogger(__name__) + if TYPE_CHECKING: from crewai.agent import Agent from crewai.agents.tools_handler import ToolsHandler @@ -541,7 +544,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): if self.agent is None: raise ValueError("Agent cannot be None") - crewai_event_bus.emit( + future = crewai_event_bus.emit( self.agent, AgentLogsExecutionEvent( agent_role=self.agent.role, @@ -551,6 +554,12 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): ), ) + if future is not None: + try: + future.result(timeout=5.0) + except Exception as e: + logger.error(f"Failed to show logs for agent execution event: {e}") + def _handle_crew_training_output( self, result: AgentFinish, human_feedback: str | None = None ) -> None: diff --git a/lib/crewai/src/crewai/events/event_listener.py b/lib/crewai/src/crewai/events/event_listener.py index 1c7602587..36c37e9c9 100644 --- a/lib/crewai/src/crewai/events/event_listener.py +++ b/lib/crewai/src/crewai/events/event_listener.py @@ -1,7 +1,6 @@ from __future__ import annotations from io import StringIO -import threading from typing import TYPE_CHECKING, Any from pydantic import Field, PrivateAttr @@ -17,8 +16,6 @@ from crewai.events.types.a2a_events import ( A2AResponseReceivedEvent, ) from crewai.events.types.agent_events import ( - AgentExecutionCompletedEvent, - AgentExecutionStartedEvent, LiteAgentExecutionCompletedEvent, LiteAgentExecutionErrorEvent, LiteAgentExecutionStartedEvent, @@ -48,7 +45,6 @@ from crewai.events.types.flow_events import ( from crewai.events.types.knowledge_events import ( KnowledgeQueryCompletedEvent, KnowledgeQueryFailedEvent, - KnowledgeQueryStartedEvent, KnowledgeRetrievalCompletedEvent, KnowledgeRetrievalStartedEvent, KnowledgeSearchQueryFailedEvent, @@ -112,7 +108,6 @@ class EventListener(BaseEventListener): text_stream: StringIO = StringIO() knowledge_retrieval_in_progress: bool = False knowledge_query_in_progress: bool = False - method_branches: dict[str, Any] = Field(default_factory=dict) def __new__(cls) -> EventListener: if cls._instance is None: @@ -126,10 +121,8 @@ class EventListener(BaseEventListener): self._telemetry = Telemetry() self._telemetry.set_tracer() self.execution_spans = {} - self.method_branches = {} self._initialized = True self.formatter = ConsoleFormatter(verbose=True) - self._crew_tree_lock = threading.Condition() # Initialize trace listener with formatter for memory event handling trace_listener = TraceCollectionListener() @@ -140,12 +133,10 @@ class EventListener(BaseEventListener): def setup_listeners(self, crewai_event_bus: CrewAIEventsBus) -> None: @crewai_event_bus.on(CrewKickoffStartedEvent) def on_crew_started(source: Any, event: CrewKickoffStartedEvent) -> None: - with self._crew_tree_lock: - self.formatter.create_crew_tree(event.crew_name or "Crew", source.id) - source._execution_span = self._telemetry.crew_execution_span( - source, event.inputs - ) - self._crew_tree_lock.notify_all() + self.formatter.handle_crew_started(event.crew_name or "Crew", source.id) + source._execution_span = self._telemetry.crew_execution_span( + source, event.inputs + ) @crewai_event_bus.on(CrewKickoffCompletedEvent) def on_crew_completed(source: Any, event: CrewKickoffCompletedEvent) -> None: @@ -153,8 +144,7 @@ class EventListener(BaseEventListener): final_string_output = event.output.raw self._telemetry.end_crew(source, final_string_output) - self.formatter.update_crew_tree( - self.formatter.current_crew_tree, + self.formatter.handle_crew_status( event.crew_name or "Crew", source.id, "completed", @@ -163,8 +153,7 @@ class EventListener(BaseEventListener): @crewai_event_bus.on(CrewKickoffFailedEvent) def on_crew_failed(source: Any, event: CrewKickoffFailedEvent) -> None: - self.formatter.update_crew_tree( - self.formatter.current_crew_tree, + self.formatter.handle_crew_status( event.crew_name or "Crew", source.id, "failed", @@ -197,23 +186,22 @@ class EventListener(BaseEventListener): # ----------- TASK EVENTS ----------- + def get_task_name(source: Any) -> str | None: + return ( + source.name + if hasattr(source, "name") and source.name + else source.description + if hasattr(source, "description") and source.description + else None + ) + @crewai_event_bus.on(TaskStartedEvent) def on_task_started(source: Any, event: TaskStartedEvent) -> None: span = self._telemetry.task_started(crew=source.agent.crew, task=source) self.execution_spans[source] = span - with self._crew_tree_lock: - self._crew_tree_lock.wait_for( - lambda: self.formatter.current_crew_tree is not None, timeout=5.0 - ) - - if self.formatter.current_crew_tree is not None: - task_name = ( - source.name if hasattr(source, "name") and source.name else None - ) - self.formatter.create_task_branch( - self.formatter.current_crew_tree, source.id, task_name - ) + task_name = get_task_name(source) + self.formatter.handle_task_started(source.id, task_name) @crewai_event_bus.on(TaskCompletedEvent) def on_task_completed(source: Any, event: TaskCompletedEvent) -> None: @@ -224,13 +212,9 @@ class EventListener(BaseEventListener): self.execution_spans[source] = None # Pass task name if it exists - task_name = source.name if hasattr(source, "name") and source.name else None - self.formatter.update_task_status( - self.formatter.current_crew_tree, - source.id, - source.agent.role, - "completed", - task_name, + task_name = get_task_name(source) + self.formatter.handle_task_status( + source.id, source.agent.role, "completed", task_name ) @crewai_event_bus.on(TaskFailedEvent) @@ -242,37 +226,12 @@ class EventListener(BaseEventListener): self.execution_spans[source] = None # Pass task name if it exists - task_name = source.name if hasattr(source, "name") and source.name else None - self.formatter.update_task_status( - self.formatter.current_crew_tree, - source.id, - source.agent.role, - "failed", - task_name, + task_name = get_task_name(source) + self.formatter.handle_task_status( + source.id, source.agent.role, "failed", task_name ) # ----------- AGENT EVENTS ----------- - - @crewai_event_bus.on(AgentExecutionStartedEvent) - def on_agent_execution_started( - _: Any, event: AgentExecutionStartedEvent - ) -> None: - self.formatter.create_agent_branch( - self.formatter.current_task_branch, - event.agent.role, - self.formatter.current_crew_tree, - ) - - @crewai_event_bus.on(AgentExecutionCompletedEvent) - def on_agent_execution_completed( - _: Any, event: AgentExecutionCompletedEvent - ) -> None: - self.formatter.update_agent_status( - self.formatter.current_agent_branch, - event.agent.role, - self.formatter.current_crew_tree, - ) - # ----------- LITE AGENT EVENTS ----------- @crewai_event_bus.on(LiteAgentExecutionStartedEvent) @@ -316,79 +275,61 @@ class EventListener(BaseEventListener): self._telemetry.flow_execution_span( event.flow_name, list(source._methods.keys()) ) - tree = self.formatter.create_flow_tree(event.flow_name, str(source.flow_id)) - self.formatter.current_flow_tree = tree - self.formatter.start_flow(event.flow_name, str(source.flow_id)) + self.formatter.handle_flow_created(event.flow_name, str(source.flow_id)) + self.formatter.handle_flow_started(event.flow_name, str(source.flow_id)) @crewai_event_bus.on(FlowFinishedEvent) def on_flow_finished(source: Any, event: FlowFinishedEvent) -> None: - self.formatter.update_flow_status( - self.formatter.current_flow_tree, event.flow_name, source.flow_id + self.formatter.handle_flow_status( + event.flow_name, + source.flow_id, ) @crewai_event_bus.on(MethodExecutionStartedEvent) def on_method_execution_started( _: Any, event: MethodExecutionStartedEvent ) -> None: - method_branch = self.method_branches.get(event.method_name) - updated_branch = self.formatter.update_method_status( - method_branch, - self.formatter.current_flow_tree, + self.formatter.handle_method_status( event.method_name, "running", ) - self.method_branches[event.method_name] = updated_branch @crewai_event_bus.on(MethodExecutionFinishedEvent) def on_method_execution_finished( _: Any, event: MethodExecutionFinishedEvent ) -> None: - method_branch = self.method_branches.get(event.method_name) - updated_branch = self.formatter.update_method_status( - method_branch, - self.formatter.current_flow_tree, + self.formatter.handle_method_status( event.method_name, "completed", ) - self.method_branches[event.method_name] = updated_branch @crewai_event_bus.on(MethodExecutionFailedEvent) def on_method_execution_failed( _: Any, event: MethodExecutionFailedEvent ) -> None: - method_branch = self.method_branches.get(event.method_name) - updated_branch = self.formatter.update_method_status( - method_branch, - self.formatter.current_flow_tree, + self.formatter.handle_method_status( event.method_name, "failed", ) - self.method_branches[event.method_name] = updated_branch @crewai_event_bus.on(MethodExecutionPausedEvent) def on_method_execution_paused( _: Any, event: MethodExecutionPausedEvent ) -> None: - method_branch = self.method_branches.get(event.method_name) - updated_branch = self.formatter.update_method_status( - method_branch, - self.formatter.current_flow_tree, + self.formatter.handle_method_status( event.method_name, "paused", ) - self.method_branches[event.method_name] = updated_branch @crewai_event_bus.on(FlowPausedEvent) def on_flow_paused(_: Any, event: FlowPausedEvent) -> None: - self.formatter.update_flow_status( - self.formatter.current_flow_tree, + self.formatter.handle_flow_status( event.flow_name, event.flow_id, "paused", ) # ----------- TOOL USAGE EVENTS ----------- - @crewai_event_bus.on(ToolUsageStartedEvent) def on_tool_usage_started(source: Any, event: ToolUsageStartedEvent) -> None: if isinstance(source, LLM): @@ -398,9 +339,9 @@ class EventListener(BaseEventListener): ) else: self.formatter.handle_tool_usage_started( - self.formatter.current_agent_branch, event.tool_name, - self.formatter.current_crew_tree, + event.tool_args, + event.run_attempts, ) @crewai_event_bus.on(ToolUsageFinishedEvent) @@ -409,12 +350,6 @@ class EventListener(BaseEventListener): self.formatter.handle_llm_tool_usage_finished( event.tool_name, ) - else: - self.formatter.handle_tool_usage_finished( - self.formatter.current_tool_branch, - event.tool_name, - self.formatter.current_crew_tree, - ) @crewai_event_bus.on(ToolUsageErrorEvent) def on_tool_usage_error(source: Any, event: ToolUsageErrorEvent) -> None: @@ -425,10 +360,9 @@ class EventListener(BaseEventListener): ) else: self.formatter.handle_tool_usage_error( - self.formatter.current_tool_branch, event.tool_name, event.error, - self.formatter.current_crew_tree, + event.run_attempts, ) # ----------- LLM EVENTS ----------- @@ -437,32 +371,15 @@ class EventListener(BaseEventListener): def on_llm_call_started(_: Any, event: LLMCallStartedEvent) -> None: self.text_stream = StringIO() self.next_chunk = 0 - # Capture the returned tool branch and update the current_tool_branch reference - thinking_branch = self.formatter.handle_llm_call_started( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, - ) - # Update the formatter's current_tool_branch to ensure proper cleanup - if thinking_branch is not None: - self.formatter.current_tool_branch = thinking_branch @crewai_event_bus.on(LLMCallCompletedEvent) def on_llm_call_completed(_: Any, event: LLMCallCompletedEvent) -> None: self.formatter.handle_llm_stream_completed() - self.formatter.handle_llm_call_completed( - self.formatter.current_tool_branch, - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, - ) @crewai_event_bus.on(LLMCallFailedEvent) def on_llm_call_failed(_: Any, event: LLMCallFailedEvent) -> None: self.formatter.handle_llm_stream_completed() - self.formatter.handle_llm_call_failed( - self.formatter.current_tool_branch, - event.error, - self.formatter.current_crew_tree, - ) + self.formatter.handle_llm_call_failed(event.error) @crewai_event_bus.on(LLMStreamChunkEvent) def on_llm_stream_chunk(_: Any, event: LLMStreamChunkEvent) -> None: @@ -473,9 +390,7 @@ class EventListener(BaseEventListener): accumulated_text = self.text_stream.getvalue() self.formatter.handle_llm_stream_chunk( - event.chunk, accumulated_text, - self.formatter.current_crew_tree, event.call_type, ) @@ -515,7 +430,6 @@ class EventListener(BaseEventListener): @crewai_event_bus.on(CrewTestCompletedEvent) def on_crew_test_completed(_: Any, event: CrewTestCompletedEvent) -> None: self.formatter.handle_crew_test_completed( - self.formatter.current_flow_tree, event.crew_name or "Crew", ) @@ -532,10 +446,7 @@ class EventListener(BaseEventListener): self.knowledge_retrieval_in_progress = True - self.formatter.handle_knowledge_retrieval_started( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, - ) + self.formatter.handle_knowledge_retrieval_started() @crewai_event_bus.on(KnowledgeRetrievalCompletedEvent) def on_knowledge_retrieval_completed( @@ -546,24 +457,13 @@ class EventListener(BaseEventListener): self.knowledge_retrieval_in_progress = False self.formatter.handle_knowledge_retrieval_completed( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, event.retrieved_knowledge, + event.query, ) - @crewai_event_bus.on(KnowledgeQueryStartedEvent) - def on_knowledge_query_started( - _: Any, event: KnowledgeQueryStartedEvent - ) -> None: - pass - @crewai_event_bus.on(KnowledgeQueryFailedEvent) def on_knowledge_query_failed(_: Any, event: KnowledgeQueryFailedEvent) -> None: - self.formatter.handle_knowledge_query_failed( - self.formatter.current_agent_branch, - event.error, - self.formatter.current_crew_tree, - ) + self.formatter.handle_knowledge_query_failed(event.error) @crewai_event_bus.on(KnowledgeQueryCompletedEvent) def on_knowledge_query_completed( @@ -575,11 +475,7 @@ class EventListener(BaseEventListener): def on_knowledge_search_query_failed( _: Any, event: KnowledgeSearchQueryFailedEvent ) -> None: - self.formatter.handle_knowledge_search_query_failed( - self.formatter.current_agent_branch, - event.error, - self.formatter.current_crew_tree, - ) + self.formatter.handle_knowledge_search_query_failed(event.error) # ----------- REASONING EVENTS ----------- @@ -587,11 +483,7 @@ class EventListener(BaseEventListener): def on_agent_reasoning_started( _: Any, event: AgentReasoningStartedEvent ) -> None: - self.formatter.handle_reasoning_started( - self.formatter.current_agent_branch, - event.attempt, - self.formatter.current_crew_tree, - ) + self.formatter.handle_reasoning_started(event.attempt) @crewai_event_bus.on(AgentReasoningCompletedEvent) def on_agent_reasoning_completed( @@ -600,14 +492,12 @@ class EventListener(BaseEventListener): self.formatter.handle_reasoning_completed( event.plan, event.ready, - self.formatter.current_crew_tree, ) @crewai_event_bus.on(AgentReasoningFailedEvent) def on_agent_reasoning_failed(_: Any, event: AgentReasoningFailedEvent) -> None: self.formatter.handle_reasoning_failed( event.error, - self.formatter.current_crew_tree, ) # ----------- AGENT LOGGING EVENTS ----------- @@ -734,18 +624,6 @@ class EventListener(BaseEventListener): event.tool_args, ) - @crewai_event_bus.on(MCPToolExecutionCompletedEvent) - def on_mcp_tool_execution_completed( - _: Any, event: MCPToolExecutionCompletedEvent - ) -> None: - self.formatter.handle_mcp_tool_execution_completed( - event.server_name, - event.tool_name, - event.tool_args, - event.result, - event.execution_duration_ms, - ) - @crewai_event_bus.on(MCPToolExecutionFailedEvent) def on_mcp_tool_execution_failed( _: Any, event: MCPToolExecutionFailedEvent diff --git a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py index c8f7000cd..6e7fba0ef 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py @@ -1,7 +1,7 @@ """Trace collection listener for orchestrating trace collection.""" import os -from typing import Any, ClassVar +from typing import Any, ClassVar, cast import uuid from typing_extensions import Self @@ -105,7 +105,7 @@ class TraceCollectionListener(BaseEventListener): """Create or return singleton instance.""" if cls._instance is None: cls._instance = super().__new__(cls) - return cls._instance + return cast(Self, cls._instance) def __init__( self, @@ -319,21 +319,12 @@ class TraceCollectionListener(BaseEventListener): source: Any, event: MemoryQueryCompletedEvent ) -> None: self._handle_action_event("memory_query_completed", source, event) - if self.formatter and self.memory_retrieval_in_progress: - self.formatter.handle_memory_query_completed( - self.formatter.current_agent_branch, - event.source_type or "memory", - event.query_time_ms, - self.formatter.current_crew_tree, - ) @event_bus.on(MemoryQueryFailedEvent) def on_memory_query_failed(source: Any, event: MemoryQueryFailedEvent) -> None: self._handle_action_event("memory_query_failed", source, event) if self.formatter and self.memory_retrieval_in_progress: self.formatter.handle_memory_query_failed( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, event.error, event.source_type or "memory", ) @@ -347,10 +338,7 @@ class TraceCollectionListener(BaseEventListener): self.memory_save_in_progress = True - self.formatter.handle_memory_save_started( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, - ) + self.formatter.handle_memory_save_started() @event_bus.on(MemorySaveCompletedEvent) def on_memory_save_completed( @@ -364,8 +352,6 @@ class TraceCollectionListener(BaseEventListener): self.memory_save_in_progress = False self.formatter.handle_memory_save_completed( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, event.save_time_ms, event.source_type or "memory", ) @@ -375,10 +361,8 @@ class TraceCollectionListener(BaseEventListener): self._handle_action_event("memory_save_failed", source, event) if self.formatter and self.memory_save_in_progress: self.formatter.handle_memory_save_failed( - self.formatter.current_agent_branch, event.error, event.source_type or "memory", - self.formatter.current_crew_tree, ) @event_bus.on(MemoryRetrievalStartedEvent) @@ -391,10 +375,7 @@ class TraceCollectionListener(BaseEventListener): self.memory_retrieval_in_progress = True - self.formatter.handle_memory_retrieval_started( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, - ) + self.formatter.handle_memory_retrieval_started() @event_bus.on(MemoryRetrievalCompletedEvent) def on_memory_retrieval_completed( @@ -406,8 +387,6 @@ class TraceCollectionListener(BaseEventListener): self.memory_retrieval_in_progress = False self.formatter.handle_memory_retrieval_completed( - self.formatter.current_agent_branch, - self.formatter.current_crew_tree, event.memory_content, event.retrieval_time_ms, ) diff --git a/lib/crewai/src/crewai/events/utils/console_formatter.py b/lib/crewai/src/crewai/events/utils/console_formatter.py index b6a5c7d6b..a395db39f 100644 --- a/lib/crewai/src/crewai/events/utils/console_formatter.py +++ b/lib/crewai/src/crewai/events/utils/console_formatter.py @@ -4,41 +4,23 @@ from typing import Any, ClassVar from rich.console import Console from rich.live import Live from rich.panel import Panel -from rich.syntax import Syntax from rich.text import Text -from rich.tree import Tree class ConsoleFormatter: - current_crew_tree: Tree | None = None - current_task_branch: Tree | None = None - current_agent_branch: Tree | None = None - current_tool_branch: Tree | None = None - current_flow_tree: Tree | None = None - current_method_branch: Tree | None = None - current_lite_agent_branch: Tree | None = None tool_usage_counts: ClassVar[dict[str, int]] = {} - current_reasoning_branch: Tree | None = None - _live_paused: bool = False - current_llm_tool_tree: Tree | None = None - current_a2a_conversation_branch: Tree | str | None = None + current_a2a_turn_count: int = 0 _pending_a2a_message: str | None = None _pending_a2a_agent_role: str | None = None _pending_a2a_turn_number: int | None = None - _a2a_turn_branches: ClassVar[dict[int, Tree]] = {} _current_a2a_agent_name: str | None = None + crew_completion_printed: ClassVar[threading.Event] = threading.Event() def __init__(self, verbose: bool = False): self.console = Console(width=None) self.verbose = verbose - # Live instance to dynamically update a Tree renderable (e.g. the Crew tree) - # When multiple Tree objects are printed sequentially we reuse this Live - # instance so the previous render is replaced instead of writing a new one. - # Once any non-Tree renderable is printed we stop the Live session so the - # final Tree persists on the terminal. - self._live: Live | None = None self._streaming_live: Live | None = None self._is_streaming: bool = False self._just_streamed_final_answer: bool = False @@ -95,98 +77,44 @@ To enable tracing, do any one of these: """Create standardized status content with consistent formatting.""" content = Text() content.append(f"{title}\n", style=f"{status_style} bold") - content.append("Name: ", style="white") + content.append("Name: \n", style="white") content.append(f"{name}\n", style=status_style) for label, value in fields.items(): - content.append(f"{label}: ", style="white") + content.append(f"{label}: \n", style="white") content.append( f"{value}\n", style=fields.get(f"{label}_style", status_style) ) - content.append("Tool Args: ", style="white") - content.append(f"{tool_args}\n", style=status_style) + if tool_args: + content.append("Tool Args: \n", style="white") + content.append(f"{tool_args}\n", style=status_style) return content - def update_tree_label( - self, - tree: Tree, - prefix: str, - name: str, - style: str = "blue", - status: str | None = None, - ) -> None: - """Update tree label with consistent formatting.""" - label = Text() - label.append(f"{prefix} ", style=f"{style} bold") - label.append(name, style=style) - if status: - label.append("\nStatus: ", style="white") - label.append(status, style=f"{style} bold") - tree.label = label - - def add_tree_node(self, parent: Tree, text: str, style: str = "yellow") -> Tree: - """Add a node to the tree with consistent styling.""" - return parent.add(Text(text, style=style)) - def print(self, *args: Any, **kwargs: Any) -> None: - """Custom print that replaces consecutive Tree renders. - - * If the argument is a single ``Tree`` instance, we either start a - ``Live`` session (first tree) or update the existing one (subsequent - trees). This results in the tree being rendered in-place instead of - being appended repeatedly to the log. - - * A blank call (no positional arguments) is ignored while a Live - session is active so it does not prematurely terminate the tree - rendering. - - * Any other renderable will terminate the Live session (if one is - active) so the last tree stays on screen and the new content is - printed normally. - """ - - # Case 1: updating / starting live Tree rendering - if len(args) == 1 and isinstance(args[0], Tree): - tree = args[0] - - if self._is_streaming: - return - - if not self._live: - # Start a new Live session for the first tree - self._live = Live(tree, console=self.console, refresh_per_second=4) - self._live.start() - else: - # Update existing Live session - self._live.update(tree, refresh=True) - return # Nothing else to do - - # Case 2: blank line while a live session is running - ignore so we - # don't break the in-place rendering behaviour - if len(args) == 0 and self._live: + """Print to console. Simplified to only handle panel-based output.""" + # Skip blank lines during streaming + if len(args) == 0 and self._is_streaming: return - - # Case 3: printing something other than a Tree → terminate live session - if self._live: - self._live.stop() - self._live = None - - # Finally, pass through to the regular Console.print implementation self.console.print(*args, **kwargs) def pause_live_updates(self) -> None: - """Pause Live session updates to allow for human input without interference.""" - if not self._live_paused: - if self._live: - self._live.stop() - self._live = None - self._live_paused = True + """Pause Live session updates to allow for human input without interference. + + This stops any active streaming Live session to prevent console refresh + interference during HITL (Human-in-the-Loop) user input. + """ + if self._streaming_live: + self._streaming_live.stop() + self._streaming_live = None def resume_live_updates(self) -> None: - """Resume Live session updates after human input is complete.""" - if self._live_paused: - self._live_paused = False + """Resume Live session updates after human input is complete. + + New streaming sessions will be created on-demand when needed. + This method exists for API compatibility with HITL callers. + """ + pass def print_panel( self, content: Text, title: str, style: str = "blue", is_flow: bool = False @@ -201,38 +129,30 @@ To enable tracing, do any one of these: self.print(panel) self.print() - def update_crew_tree( + def handle_crew_status( self, - tree: Tree | None, crew_name: str, source_id: str, status: str = "completed", final_string_output: str = "", ) -> None: - """Handle crew tree updates with consistent formatting.""" - if not self.verbose or tree is None: + """Handle crew completion/failure with panel display.""" + if not self.verbose: return if status == "completed": - prefix, style = "✅ Crew:", "green" + style = "green" title = "Crew Completion" content_title = "Crew Execution Completed" elif status == "failed": - prefix, style = "❌ Crew:", "red" + style = "red" title = "Crew Failure" content_title = "Crew Execution Failed" else: - prefix, style = "🚀 Crew:", "cyan" + style = "cyan" title = "Crew Execution" content_title = "Crew Execution Started" - self.update_tree_label( - tree, - prefix, - crew_name or "Crew", - style, - ) - content = self.create_status_content( content_title, crew_name or "Crew", @@ -243,29 +163,23 @@ To enable tracing, do any one of these: if status == "failed" and final_string_output: content.append("Error:\n", style="white bold") content.append(f"{final_string_output}\n", style="red") - else: + elif final_string_output: content.append(f"Final Output: {final_string_output}\n", style="white") self.print_panel(content, title, style) if status in ["completed", "failed"]: self.crew_completion_printed.set() - - # Show tracing disabled message after crew completion self._show_tracing_disabled_message_if_needed() - def create_crew_tree(self, crew_name: str, source_id: str) -> Tree | None: - """Create and initialize a new crew tree with initial status.""" + def handle_crew_started(self, crew_name: str, source_id: str) -> None: + """Show crew started panel.""" if not self.verbose: - return None + return # Reset the crew completion event for this new crew execution ConsoleFormatter.crew_completion_printed.clear() - tree = Tree( - Text("🚀 Crew: ", style="cyan bold") + Text(crew_name, style="cyan") - ) - content = self.create_status_content( "Crew Execution Started", crew_name, @@ -273,219 +187,85 @@ To enable tracing, do any one of these: ID=source_id, ) - self.print_panel(content, "Crew Execution Started", "cyan") + self.print_panel(content, "🚀 Crew Execution Started", "cyan") - # Set the current_crew_tree attribute directly - self.current_crew_tree = tree - - return tree - - def create_task_branch( - self, crew_tree: Tree | None, task_id: str, task_name: str | None = None - ) -> Tree | None: - """Create and initialize a task branch.""" + def handle_task_started(self, task_id: str, task_name: str | None = None) -> None: + """Show task started panel.""" if not self.verbose: - return None + return - task_content = Text() + content = Text() + display_name = task_name if task_name else task_id - # Display task name if available, otherwise just the ID - if task_name: - task_content.append("📋 Task: ", style="yellow bold") - task_content.append(f"{task_name}", style="yellow bold") - task_content.append(f" (ID: {task_id})", style="yellow dim") - else: - task_content.append(f"📋 Task: {task_id}", style="yellow bold") + content.append("Task Started\n", style="yellow bold") + content.append("Name: ", style="white") + content.append(f"{display_name}\n", style="yellow") + content.append("ID: ", style="white") + content.append(f"{task_id}\n", style="yellow ") - task_content.append("\nStatus: ", style="white") - task_content.append("Executing Task...", style="yellow dim") + self.print_panel(content, "📋 Task Started", "yellow") - task_branch = None - if crew_tree: - task_branch = crew_tree.add(task_content) - self.print(crew_tree) - else: - self.print_panel(task_content, "Task Started", "yellow") - - self.print() - - # Set the current_task_branch attribute directly - self.current_task_branch = task_branch - - return task_branch - - def update_task_status( + def handle_task_status( self, - crew_tree: Tree | None, task_id: str, agent_role: str, status: str = "completed", task_name: str | None = None, ) -> None: - """Update task status in the tree.""" - if not self.verbose or crew_tree is None: + """Show task completion/failure panel.""" + if not self.verbose: return if status == "completed": style = "green" - status_text = "✅ Completed" - panel_title = "Task Completion" + panel_title = "📋 Task Completion" else: style = "red" - status_text = "❌ Failed" - panel_title = "Task Failure" + panel_title = "📋 Task Failure" - # Update tree label - for branch in crew_tree.children: - if str(task_id) in str(branch.label): - # Build label without introducing stray blank lines - task_content = Text() - # First line: Task ID/name - if task_name: - task_content.append("📋 Task: ", style=f"{style} bold") - task_content.append(f"{task_name}", style=f"{style} bold") - task_content.append(f" (ID: {task_id})", style=f"{style} dim") - else: - task_content.append(f"📋 Task: {task_id}", style=f"{style} bold") - - # Second line: Assigned to - task_content.append("\nAssigned to: ", style="white") - task_content.append(agent_role, style=style) - - # Third line: Status - task_content.append("\nStatus: ", style="white") - task_content.append(status_text, style=f"{style} bold") - branch.label = task_content - self.print(crew_tree) - break - - # Show status panel display_name = task_name if task_name else str(task_id) content = self.create_status_content( f"Task {status.title()}", display_name, style, Agent=agent_role ) self.print_panel(content, panel_title, style) - def create_agent_branch( - self, task_branch: Tree | None, agent_role: str, crew_tree: Tree | None - ) -> Tree | None: - """Create and initialize an agent branch.""" - if not self.verbose or not task_branch or not crew_tree: - return None - - # Instead of creating a separate Agent node, we treat the task branch - # itself as the logical agent branch so that Reasoning/Tool nodes are - # nested under the task without an extra visual level. - - # Store the task branch as the current_agent_branch for future nesting. - self.current_agent_branch = task_branch - - # No additional tree modification needed; return the task branch so - # caller logic remains unchanged. - return task_branch - - def update_agent_status( - self, - agent_branch: Tree | None, - agent_role: str, - crew_tree: Tree | None, - status: str = "completed", - ) -> None: - """Update agent status in the tree.""" - # We no longer render a separate agent branch, so this method simply - # updates the stored branch reference (already the task branch) without - # altering the tree. Keeping it a no-op avoids duplicate status lines. - return - - def create_flow_tree(self, flow_name: str, flow_id: str) -> Tree | None: - """Create and initialize a flow tree.""" + def handle_flow_created(self, flow_name: str, flow_id: str) -> None: + """Show flow started panel.""" content = self.create_status_content( "Starting Flow Execution", flow_name, "blue", ID=flow_id ) - self.print_panel(content, "Flow Execution", "blue", is_flow=True) + self.print_panel(content, "🌊 Flow Execution", "blue", is_flow=True) - # Create initial tree with flow ID - flow_label = Text() - flow_label.append("🌊 Flow: ", style="blue bold") - flow_label.append(flow_name, style="blue") - flow_label.append("\nID: ", style="white") - flow_label.append(flow_id, style="blue") + def handle_flow_started(self, flow_name: str, flow_id: str) -> None: + """Show flow started panel.""" + content = Text() + content.append("Flow Started\n", style="blue bold") + content.append("Name: ", style="white") + content.append(f"{flow_name}\n", style="blue") + content.append("ID: ", style="white") + content.append(f"{flow_id}\n", style="blue") - flow_tree = Tree(flow_label) - self.add_tree_node(flow_tree, "✨ Created", "blue") - self.add_tree_node(flow_tree, "✅ Initialization Complete", "green") + self.print_panel(content, "🌊 Flow Started", "blue", is_flow=True) - return flow_tree - - def start_flow(self, flow_name: str, flow_id: str) -> Tree | None: - """Initialize or update a flow execution tree.""" - if self.current_flow_tree is not None: - for child in self.current_flow_tree.children: - if "Starting Flow" in str(child.label): - child.label = Text("🚀 Flow Started", style="green") - break - return self.current_flow_tree - - flow_tree = Tree("") - flow_label = Text() - flow_label.append("🌊 Flow: ", style="blue bold") - flow_label.append(flow_name, style="blue") - flow_label.append("\nID: ", style="white") - flow_label.append(flow_id, style="blue") - flow_tree.label = flow_label - - self.add_tree_node(flow_tree, "🧠 Starting Flow...", "yellow") - - self.print(flow_tree) - self.print() - - self.current_flow_tree = flow_tree - return flow_tree - - def update_flow_status( + def handle_flow_status( self, - flow_tree: Tree | None, flow_name: str, flow_id: str, status: str = "completed", ) -> None: - """Update flow status in the tree.""" - if flow_tree is None: - return - - # Determine status-specific labels and styles + """Show flow status panel.""" if status == "completed": - label_prefix = "✅ Flow Finished:" style = "green" - node_text = "✅ Flow Completed" content_text = "Flow Execution Completed" - panel_title = "Flow Completion" + panel_title = "✅ Flow Completion" elif status == "paused": - label_prefix = "⏳ Flow Paused:" style = "cyan" - node_text = "⏳ Waiting for Human Feedback" content_text = "Flow Paused - Waiting for Feedback" - panel_title = "Flow Paused" + panel_title = "⏳ Flow Paused" else: - label_prefix = "❌ Flow Failed:" style = "red" - node_text = "❌ Flow Failed" content_text = "Flow Execution Failed" - panel_title = "Flow Failure" - - # Update main flow label - self.update_tree_label( - flow_tree, - label_prefix, - flow_name, - style, - ) - - # Update initialization node status - for child in flow_tree.children: - if "Starting Flow" in str(child.label): - child.label = Text(node_text, style=style) - break + panel_title = "❌ Flow Failure" content = self.create_status_content( content_text, @@ -493,405 +273,141 @@ To enable tracing, do any one of these: style, ID=flow_id, ) - self.print(flow_tree) - self.print_panel(content, panel_title, style) + self.print_panel(content, panel_title, style, is_flow=True) - def update_method_status( + def handle_method_status( self, - method_branch: Tree | None, - flow_tree: Tree | None, method_name: str, status: str = "running", - ) -> Tree | None: - """Update method status in the flow tree.""" - if not flow_tree: - return None + ) -> None: + """Show method status panel.""" + if not self.verbose: + return if status == "running": - prefix, style = "🔄 Running:", "yellow" + style = "yellow" + panel_title = "🔄 Flow Method Running" elif status == "completed": - prefix, style = "✅ Completed:", "green" - for child in flow_tree.children: - if "Starting Flow" in str(child.label): - child.label = Text("Flow Method Step", style="white") - break + style = "green" + panel_title = "✅ Flow Method Completed" elif status == "paused": - prefix, style = "⏳ Paused:", "cyan" - for child in flow_tree.children: - if "Starting Flow" in str(child.label): - child.label = Text("⏳ Waiting for Feedback", style="cyan") - break + style = "cyan" + panel_title = "⏳ Flow Method Paused" else: - prefix, style = "❌ Failed:", "red" - for child in flow_tree.children: - if "Starting Flow" in str(child.label): - child.label = Text("❌ Flow Step Failed", style="red") - break + style = "red" + panel_title = "❌ Flow Method Failed" - if method_branch is not None: - if method_branch in flow_tree.children: - method_branch.label = Text(prefix, style=f"{style} bold") + Text( - f" {method_name}", style=style - ) - self.print(flow_tree) - self.print() - return method_branch + content = Text() + content.append(f"Method: {method_name}\n", style=f"{style} bold") + content.append("Status: ", style="white") + content.append(f"{status.title()}\n", style=style) - for branch in flow_tree.children: - label_str = str(branch.label) - if f" {method_name}" in label_str and ( - "Running:" in label_str - or "Completed:" in label_str - or "Failed:" in label_str - ): - method_branch = branch - break - - if method_branch is None: - method_branch = flow_tree.add("") - - method_branch.label = Text(prefix, style=f"{style} bold") + Text( - f" {method_name}", style=style - ) - - self.print(flow_tree) - self.print() - - return method_branch - - def get_llm_tree(self, tool_name: str) -> Tree: - text = Text() - text.append(f"🔧 Using {tool_name} from LLM available_function", style="yellow") - - tree = self.current_flow_tree or self.current_crew_tree - - if tree: - tree.add(text) - - return tree or Tree(text) + self.print_panel(content, panel_title, style, is_flow=True) def handle_llm_tool_usage_started( self, tool_name: str, tool_args: dict[str, Any] | str, - ) -> Tree: - # Create status content for the tool usage + ) -> None: + """Handle LLM tool usage started with panel display.""" content = self.create_status_content( "Tool Usage Started", tool_name, Status="In Progress", tool_args=tool_args ) - - # Create and print the panel - self.print_panel(content, "Tool Usage", "green") - self.print() - - # Still return the tree for compatibility with existing code - return self.get_llm_tree(tool_name) + self.print_panel(content, "🔧 LLM Tool Usage", "yellow") def handle_llm_tool_usage_finished( self, tool_name: str, ) -> None: - tree = self.get_llm_tree(tool_name) - self.add_tree_node(tree, "✅ Tool Usage Completed", "green") - self.print(tree) - self.print() + """Handle LLM tool usage finished with panel display.""" + content = Text() + content.append("Tool Usage Completed\n", style="green bold") + content.append("Tool: ", style="white") + content.append(f"{tool_name}\n", style="green") + + self.print_panel(content, "✅ LLM Tool Completed", "green") def handle_llm_tool_usage_error( self, tool_name: str, error: str, ) -> None: - tree = self.get_llm_tree(tool_name) - self.add_tree_node(tree, "❌ Tool Usage Failed", "red") - self.print(tree) - self.print() - + """Handle LLM tool usage error with panel display.""" error_content = self.create_status_content( "Tool Usage Failed", tool_name, "red", Error=error ) - self.print_panel(error_content, "Tool Error", "red") + self.print_panel(error_content, "❌ LLM Tool Error", "red") def handle_tool_usage_started( self, - agent_branch: Tree | None, tool_name: str, - crew_tree: Tree | None, tool_args: dict[str, Any] | str = "", - ) -> Tree | None: - """Handle tool usage started event.""" + run_attempts: int | None = None, + ) -> None: + """Handle tool usage started event with panel display.""" if not self.verbose: - return None - - # Parent for tool usage: LiteAgent > Agent > Task - branch_to_use = ( - self.current_lite_agent_branch or agent_branch or self.current_task_branch - ) - - # Render full crew tree when available for consistent live updates - tree_to_use = self.current_crew_tree or crew_tree or branch_to_use - - if branch_to_use is None or tree_to_use is None: - # If we don't have a valid branch, default to crew_tree if provided - if crew_tree is not None: - branch_to_use = tree_to_use = crew_tree - else: - return None + return # Update tool usage count self.tool_usage_counts[tool_name] = self.tool_usage_counts.get(tool_name, 0) + 1 + iteration = self.tool_usage_counts[tool_name] - # Find or create tool node - tool_branch = self.current_tool_branch - if tool_branch is None: - tool_branch = branch_to_use.add("") - self.current_tool_branch = tool_branch + content = Text() + content.append("Tool: ", style="white") + content.append(f"{tool_name}\n", style="yellow bold") - # Update label with current count - self.update_tree_label( - tool_branch, - "🔧", - f"Using {tool_name} ({self.tool_usage_counts[tool_name]})", - "yellow", - ) + if tool_args: + content.append("Args: ", style="white") + args_str = ( + str(tool_args)[:200] + "..." + if len(str(tool_args)) > 200 + else str(tool_args) + ) + content.append(f"{args_str}\n", style="yellow ") - # Print updated tree immediately - self.print(tree_to_use) - self.print() - - return tool_branch - - def handle_tool_usage_finished( - self, - tool_branch: Tree | None, - tool_name: str, - crew_tree: Tree | None, - ) -> None: - """Handle tool usage finished event.""" - if not self.verbose or tool_branch is None: - return - - # Decide which tree to render: prefer full crew tree, else parent branch - tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch - if tree_to_use is None: - return - - # Update the existing tool node's label - self.update_tree_label( - tool_branch, - "🔧", - f"Used {tool_name} ({self.tool_usage_counts[tool_name]})", - "green", - ) - - # Clear the current tool branch as we're done with it - self.current_tool_branch = None - - # Only print if we have a valid tree and the tool node is still in it - if isinstance(tree_to_use, Tree) and tool_branch in tree_to_use.children: - self.print(tree_to_use) - self.print() + self.print_panel(content, f"🔧 Tool Execution Started (#{iteration})", "yellow") def handle_tool_usage_error( self, - tool_branch: Tree | None, tool_name: str, error: str, - crew_tree: Tree | None, + run_attempts: int | None = None, ) -> None: - """Handle tool usage error event.""" + """Handle tool usage error event with panel display.""" if not self.verbose: return - # Decide which tree to render: prefer full crew tree, else parent branch - tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch + iteration = self.tool_usage_counts.get(tool_name, 1) - if tool_branch: - self.update_tree_label( - tool_branch, - "🔧 Failed", - f"{tool_name} ({self.tool_usage_counts[tool_name]})", - "red", - ) - if tree_to_use: - self.print(tree_to_use) - self.print() + content = Text() + content.append("Tool Failed\n", style="red bold") + content.append("Tool: ", style="white") + content.append(f"{tool_name}\n", style="red bold") + content.append("Iteration: ", style="white") + content.append(f"{iteration}\n", style="red") + if run_attempts is not None: + content.append("Attempt: ", style="white") + content.append(f"{run_attempts}\n", style="red") + content.append("Error: ", style="white") + content.append(f"{error}\n", style="red") - # Show error panel - error_content = self.create_status_content( - "Tool Usage Failed", tool_name, "red", Error=error - ) - self.print_panel(error_content, "Tool Error", "red") + self.print_panel(content, f"🔧 Tool Error (#{iteration})", "red") - def handle_llm_call_started( - self, - agent_branch: Tree | None, - crew_tree: Tree | None, - ) -> Tree | None: - """Handle LLM call started event.""" - if not self.verbose: - return None - - # Parent for tool usage: LiteAgent > Agent > Task - branch_to_use = ( - self.current_lite_agent_branch or agent_branch or self.current_task_branch - ) - - # Render full crew tree when available for consistent live updates - tree_to_use = self.current_crew_tree or crew_tree or branch_to_use - - if branch_to_use is None or tree_to_use is None: - # If we don't have a valid branch, default to crew_tree if provided - if crew_tree is not None: - branch_to_use = tree_to_use = crew_tree - else: - return None - - # Only add thinking status if we don't have a current tool branch - # or if the current tool branch is not a thinking node - should_add_thinking = self.current_tool_branch is None or "Thinking" not in str( - self.current_tool_branch.label - ) - - if should_add_thinking: - tool_branch = branch_to_use.add("") - self.update_tree_label(tool_branch, "🧠", "Thinking...", "blue") - self.current_tool_branch = tool_branch - self.print(tree_to_use) - self.print() - return tool_branch - - # Return the existing tool branch if it's already a thinking node - return self.current_tool_branch - - def handle_llm_call_completed( - self, - tool_branch: Tree | None, - agent_branch: Tree | None, - crew_tree: Tree | None, - ) -> None: - """Handle LLM call completed event.""" + def handle_llm_call_failed(self, error: str) -> None: + """Handle LLM call failed event with panel display.""" if not self.verbose: return - # Decide which tree to render: prefer full crew tree, else parent branch - tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch - if tree_to_use is None: - return - - # Try to remove the thinking status node - first try the provided tool_branch - thinking_branch_to_remove = None - removed = False - - # Method 1: Use the provided tool_branch if it's a thinking/streaming node - if tool_branch is not None and ( - "Thinking" in str(tool_branch.label) - or "Streaming" in str(tool_branch.label) - ): - thinking_branch_to_remove = tool_branch - - # Method 2: Fallback - search for any thinking/streaming node if tool_branch is None or not found - if thinking_branch_to_remove is None: - parents = [ - self.current_lite_agent_branch, - self.current_agent_branch, - self.current_task_branch, - tree_to_use, - ] - for parent in parents: - if isinstance(parent, Tree): - for child in parent.children: - label_str = str(child.label) - if "Thinking" in label_str or "Streaming" in label_str: - thinking_branch_to_remove = child - break - if thinking_branch_to_remove: - break - - # Remove the thinking node if found - if thinking_branch_to_remove: - parents = [ - self.current_lite_agent_branch, - self.current_agent_branch, - self.current_task_branch, - tree_to_use, - ] - for parent in parents: - if ( - isinstance(parent, Tree) - and thinking_branch_to_remove in parent.children - ): - parent.children.remove(thinking_branch_to_remove) - removed = True - break - - # Clear pointer if we just removed the current_tool_branch - if self.current_tool_branch is thinking_branch_to_remove: - self.current_tool_branch = None - - if removed: - self.print(tree_to_use) - self.print() - - def handle_llm_call_failed( - self, tool_branch: Tree | None, error: str, crew_tree: Tree | None - ) -> None: - """Handle LLM call failed event.""" - if not self.verbose: - return - - # Decide which tree to render: prefer full crew tree, else parent branch - tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch - - # Find the thinking branch to update (similar to completion logic) - thinking_branch_to_update = None - - if tool_branch is not None and ( - "Thinking" in str(tool_branch.label) - or "Streaming" in str(tool_branch.label) - ): - thinking_branch_to_update = tool_branch - - # Method 2: Fallback - search for any thinking/streaming node if tool_branch is None or not found - if thinking_branch_to_update is None: - parents = [ - self.current_lite_agent_branch, - self.current_agent_branch, - self.current_task_branch, - tree_to_use, - ] - for parent in parents: - if isinstance(parent, Tree): - for child in parent.children: - label_str = str(child.label) - if "Thinking" in label_str or "Streaming" in label_str: - thinking_branch_to_update = child - break - if thinking_branch_to_update: - break - - # Update the thinking branch to show failure - if thinking_branch_to_update: - thinking_branch_to_update.label = Text("❌ LLM Failed", style="red bold") - # Clear the current_tool_branch reference - if self.current_tool_branch is thinking_branch_to_update: - self.current_tool_branch = None - if tree_to_use: - self.print(tree_to_use) - self.print() - - # Show error panel error_content = Text() - error_content.append("❌ LLM Call Failed\n", style="red bold") + error_content.append("LLM Call Failed\n", style="red bold") error_content.append("Error: ", style="white") error_content.append(str(error), style="red") - self.print_panel(error_content, "LLM Error", "red") + self.print_panel(error_content, "❌ LLM Error", "red") def handle_llm_stream_chunk( self, - chunk: str, accumulated_text: str, - crew_tree: Tree | None, call_type: Any = None, ) -> None: """Handle LLM stream chunk event - display streaming text in a panel. @@ -899,7 +415,7 @@ To enable tracing, do any one of these: Args: chunk: The new chunk of text received. accumulated_text: All text accumulated so far. - crew_tree: The current crew tree for rendering. + crew_tree: Unused (kept for API compatibility). call_type: The type of LLM call (LLM_CALL or TOOL_CALL). """ if not self.verbose: @@ -908,10 +424,6 @@ To enable tracing, do any one of these: self._is_streaming = True self._last_stream_call_type = call_type - if self._live: - self._live.stop() - self._live = None - display_text = accumulated_text max_lines = 20 lines = display_text.split("\n") @@ -966,65 +478,29 @@ To enable tracing, do any one of these: def handle_crew_test_started( self, crew_name: str, source_id: str, n_iterations: int - ) -> Tree | None: - """Handle crew test started event.""" - if not self.verbose: - return None - - # Create initial panel - content = Text() - content.append("🧪 Starting Crew Test\n\n", style="blue bold") - content.append("Crew: ", style="white") - content.append(f"{crew_name}\n", style="blue") - content.append("ID: ", style="white") - content.append(str(source_id), style="blue") - content.append("\nIterations: ", style="white") - content.append(str(n_iterations), style="yellow") - - self.print() - self.print_panel(content, "Test Execution", "blue") - self.print() - - # Create and display the test tree - test_label = Text() - test_label.append("🧪 Test: ", style="blue bold") - test_label.append(crew_name or "Crew", style="blue") - test_label.append("\nStatus: ", style="white") - test_label.append("In Progress", style="yellow") - - test_tree = Tree(test_label) - self.add_tree_node(test_tree, "🔄 Running tests...", "yellow") - - self.print(test_tree) - self.print() - return test_tree - - def handle_crew_test_completed( - self, flow_tree: Tree | None, crew_name: str ) -> None: - """Handle crew test completed event.""" + """Handle crew test started event with panel display.""" if not self.verbose: return - if flow_tree: - # Update test tree label to show completion - test_label = Text() - test_label.append("✅ Test: ", style="green bold") - test_label.append(crew_name or "Crew", style="green") - test_label.append("\nStatus: ", style="white") - test_label.append("Completed", style="green bold") - flow_tree.label = test_label + content = Text() + content.append("Starting Crew Test\n", style="blue bold") + content.append("Crew: ", style="white") + content.append(f"{crew_name}\n", style="blue") + content.append("ID: ", style="white") + content.append(f"{source_id}\n", style="blue") + content.append("Iterations: ", style="white") + content.append(f"{n_iterations}\n", style="yellow") + content.append("Status: ", style="white") + content.append("Running...", style="yellow") - # Update the running tests node - for child in flow_tree.children: - if "Running tests" in str(child.label): - child.label = Text("✅ Tests completed successfully", style="green") - break + self.print_panel(content, "🧪 Test Execution Started", "blue") - self.print(flow_tree) - self.print() + def handle_crew_test_completed(self, crew_name: str) -> None: + """Handle crew test completed event with panel display.""" + if not self.verbose: + return - # Create completion panel completion_content = Text() completion_content.append("Test Execution Completed\n", style="green bold") completion_content.append("Crew: ", style="white") @@ -1090,68 +566,50 @@ To enable tracing, do any one of these: self.print_panel(failure_content, "Test Failure", "red") self.print() - def create_lite_agent_branch(self, lite_agent_role: str) -> Tree | None: - """Create and initialize a lite agent branch.""" + def create_lite_agent_branch(self, lite_agent_role: str) -> None: + """Show lite agent started panel.""" if not self.verbose: - return None + return - # Create initial tree for LiteAgent if it doesn't exist - if not self.current_lite_agent_branch: - lite_agent_label = Text() - lite_agent_label.append("🤖 LiteAgent: ", style="cyan bold") - lite_agent_label.append(lite_agent_role, style="cyan") - lite_agent_label.append("\nStatus: ", style="white") - lite_agent_label.append("In Progress", style="yellow") + content = Text() + content.append("LiteAgent Started\n", style="cyan bold") + content.append("Role: ", style="white") + content.append(f"{lite_agent_role}\n", style="cyan") + content.append("Status: ", style="white") + content.append("In Progress\n", style="yellow") - lite_agent_tree = Tree(lite_agent_label) - self.current_lite_agent_branch = lite_agent_tree - self.print(lite_agent_tree) - self.print() - - return self.current_lite_agent_branch + self.print_panel(content, "🤖 LiteAgent Started", "cyan") def update_lite_agent_status( self, - lite_agent_branch: Tree | None, lite_agent_role: str, status: str = "completed", **fields: dict[str, Any], ) -> None: - """Update lite agent status in the tree.""" - if not self.verbose or lite_agent_branch is None: + """Show lite agent status panel.""" + if not self.verbose: return - # Determine style based on status if status == "completed": - prefix, style = "✅ LiteAgent:", "green" - status_text = "Completed" - title = "LiteAgent Completion" + style = "green" + title = "✅ LiteAgent Completed" elif status == "failed": - prefix, style = "❌ LiteAgent:", "red" - status_text = "Failed" - title = "LiteAgent Error" + style = "red" + title = "❌ LiteAgent Failed" else: - prefix, style = "🤖 LiteAgent:", "yellow" - status_text = "In Progress" - title = "LiteAgent Status" + style = "yellow" + title = "🤖 LiteAgent Status" - # Update the tree label - lite_agent_label = Text() - lite_agent_label.append(f"{prefix} ", style=f"{style} bold") - lite_agent_label.append(lite_agent_role, style=style) - lite_agent_label.append("\nStatus: ", style="white") - lite_agent_label.append(status_text, style=f"{style} bold") - lite_agent_branch.label = lite_agent_label + content = Text() + content.append(f"LiteAgent {status.title()}\n", style=f"{style} bold") + content.append("Role: ", style="white") + content.append(f"{lite_agent_role}\n", style=style) - self.print(lite_agent_branch) - self.print() + for field_name, field_value in fields.items(): + content.append(f"{field_name}: ", style="white") + content.append(f"{field_value}\n", style=style) - # Show status panel if additional fields are provided - if fields: - content = self.create_status_content( - f"LiteAgent {status.title()}", lite_agent_role, style, **fields - ) - self.print_panel(content, title, style) + self.print_panel(content, title, style) def handle_lite_agent_execution( self, @@ -1160,346 +618,174 @@ To enable tracing, do any one of these: error: Any = None, **fields: dict[str, Any], ) -> None: - """Handle lite agent execution events with consistent formatting.""" + """Handle lite agent execution events with panel display.""" if not self.verbose: return if status == "started": - # Create or get the LiteAgent branch - lite_agent_branch = self.create_lite_agent_branch(lite_agent_role) - if lite_agent_branch and fields: - # Show initial status panel + self.create_lite_agent_branch(lite_agent_role) + if fields: content = self.create_status_content( "LiteAgent Session Started", lite_agent_role, "cyan", **fields ) - self.print_panel(content, "LiteAgent Started", "cyan") + self.print_panel(content, "🤖 LiteAgent Started", "cyan") else: - # Update existing LiteAgent branch if error: fields["Error"] = error - self.update_lite_agent_status( - self.current_lite_agent_branch, lite_agent_role, status, **fields - ) + self.update_lite_agent_status(lite_agent_role, status, **fields) def handle_knowledge_retrieval_started( self, - agent_branch: Tree | None, - crew_tree: Tree | None, - ) -> Tree | None: - """Handle knowledge retrieval started event.""" + ) -> None: + """Handle knowledge retrieval started event with panel display.""" if not self.verbose: - return None + return - branch_to_use = agent_branch or self.current_lite_agent_branch - tree_to_use = branch_to_use or crew_tree + content = Text() + content.append("Knowledge Retrieval Started\n", style="blue bold") + content.append("Status: ", style="white") + content.append("Retrieving...\n", style="blue") - if branch_to_use is None or tree_to_use is None: - # If we don't have a valid branch, default to crew_tree if provided - if crew_tree is not None: - branch_to_use = tree_to_use = crew_tree - else: - return None - - knowledge_branch = branch_to_use.add("") - self.update_tree_label( - knowledge_branch, "🔍", "Knowledge Retrieval Started", "blue" - ) - - self.print(tree_to_use) - self.print() - return knowledge_branch + self.print_panel(content, "🔍 Knowledge Retrieval", "blue") def handle_knowledge_retrieval_completed( self, - agent_branch: Tree | None, - crew_tree: Tree | None, retrieved_knowledge: Any, + search_query: str, ) -> None: - """Handle knowledge retrieval completed event.""" + """Handle knowledge retrieval completed event with panel display.""" if not self.verbose: return - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree - - if branch_to_use is None and tree_to_use is not None: - branch_to_use = tree_to_use - - if branch_to_use is None or tree_to_use is None: - if retrieved_knowledge: - knowledge_text = str(retrieved_knowledge) - if len(knowledge_text) > 500: - knowledge_text = knowledge_text[:497] + "..." - - knowledge_panel = Panel( - Text(knowledge_text, style="white"), - title="📚 Retrieved Knowledge", - border_style="green", - padding=(1, 2), - ) - self.print(knowledge_panel) - self.print() - return - - knowledge_branch_found = False - for child in branch_to_use.children: - if "Knowledge Retrieval Started" in str(child.label): - self.update_tree_label( - child, "✅", "Knowledge Retrieval Completed", "green" - ) - knowledge_branch_found = True - break - - if not knowledge_branch_found: - for child in branch_to_use.children: - if ( - "Knowledge Retrieval" in str(child.label) - and "Started" not in str(child.label) - and "Completed" not in str(child.label) - ): - self.update_tree_label( - child, "✅", "Knowledge Retrieval Completed", "green" - ) - knowledge_branch_found = True - break - - if not knowledge_branch_found: - knowledge_branch = branch_to_use.add("") - self.update_tree_label( - knowledge_branch, "✅", "Knowledge Retrieval Completed", "green" - ) - - self.print(tree_to_use) - + content = Text() + content.append("Search Query:\n", style="white") + content.append(f"{search_query}\n", style="green") + content.append("Knowledge Retrieved: \n", style="white") if retrieved_knowledge: knowledge_text = str(retrieved_knowledge) if len(knowledge_text) > 500: knowledge_text = knowledge_text[:497] + "..." + content.append(f"{knowledge_text}\n", style="green ") + else: + content.append("No knowledge retrieved\n", style="yellow") - knowledge_panel = Panel( - Text(knowledge_text, style="white"), - title="📚 Retrieved Knowledge", - border_style="green", - padding=(1, 2), - ) - self.print(knowledge_panel) - - self.print() + self.print_panel(content, "📚 Knowledge Retrieved", "green") def handle_knowledge_query_started( self, - agent_branch: Tree | None, task_prompt: str, - crew_tree: Tree | None, ) -> None: - """Handle knowledge query generated event.""" + """Handle knowledge query started event with panel display.""" if not self.verbose: return - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree - if branch_to_use is None or tree_to_use is None: - return - - query_branch = branch_to_use.add("") - self.update_tree_label( - query_branch, "🔎", f"Query: {task_prompt[:50]}...", "yellow" + content = Text() + content.append("Knowledge Query Started\n", style="yellow bold") + content.append("Query: ", style="white") + query_preview = ( + task_prompt[:100] + "..." if len(task_prompt) > 100 else task_prompt ) + content.append(f"{query_preview}\n", style="yellow") - self.print(tree_to_use) - self.print() + self.print_panel(content, "🔎 Knowledge Query", "yellow") def handle_knowledge_query_failed( self, - agent_branch: Tree | None, error: str, - crew_tree: Tree | None, ) -> None: - """Handle knowledge query failed event.""" + """Handle knowledge query failed event with panel display.""" if not self.verbose: return - tree_to_use = self.current_lite_agent_branch or crew_tree - branch_to_use = self.current_lite_agent_branch or agent_branch - - if branch_to_use and tree_to_use: - query_branch = branch_to_use.add("") - self.update_tree_label(query_branch, "❌", "Knowledge Query Failed", "red") - self.print(tree_to_use) - self.print() - - # Show error panel error_content = self.create_status_content( "Knowledge Query Failed", "Query Error", "red", Error=error ) - self.print_panel(error_content, "Knowledge Error", "red") + self.print_panel(error_content, "❌ Knowledge Error", "red") - def handle_knowledge_query_completed( - self, - agent_branch: Tree | None, - crew_tree: Tree | None, - ) -> None: - """Handle knowledge query completed event.""" + def handle_knowledge_query_completed(self) -> None: + """Handle knowledge query completed event with panel display.""" if not self.verbose: return - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree + content = Text() + content.append("Knowledge Query Completed\n", style="green bold") - if branch_to_use is None or tree_to_use is None: - return - - query_branch = branch_to_use.add("") - self.update_tree_label(query_branch, "✅", "Knowledge Query Completed", "green") - - self.print(tree_to_use) - self.print() + self.print_panel(content, "✅ Knowledge Query Complete", "green") def handle_knowledge_search_query_failed( self, - agent_branch: Tree | None, error: str, - crew_tree: Tree | None, ) -> None: - """Handle knowledge search query failed event.""" + """Handle knowledge search query failed event with panel display.""" if not self.verbose: return - tree_to_use = self.current_lite_agent_branch or crew_tree - branch_to_use = self.current_lite_agent_branch or agent_branch - - if branch_to_use and tree_to_use: - query_branch = branch_to_use.add("") - self.update_tree_label(query_branch, "❌", "Knowledge Search Failed", "red") - self.print(tree_to_use) - self.print() - - # Show error panel error_content = self.create_status_content( "Knowledge Search Failed", "Search Error", "red", Error=error ) - self.print_panel(error_content, "Search Error", "red") + self.print_panel(error_content, "❌ Search Error", "red") # ----------- AGENT REASONING EVENTS ----------- def handle_reasoning_started( self, - agent_branch: Tree | None, attempt: int, - crew_tree: Tree | None, - ) -> Tree | None: - """Handle agent reasoning started (or refinement) event.""" + ) -> None: + """Handle agent reasoning started event with panel display.""" if not self.verbose: - return None + return - # Prefer LiteAgent > Agent > Task branch as the parent for reasoning - branch_to_use = ( - self.current_lite_agent_branch or agent_branch or self.current_task_branch + content = Text() + content.append("Reasoning Started\n", style="blue bold") + content.append("Attempt: ", style="white") + content.append(f"{attempt}\n", style="blue") + content.append("Status: ", style="white") + content.append("Thinking...\n", style="blue") + + panel_title = ( + f"🧠 Reasoning (Attempt #{attempt})" if attempt > 1 else "🧠 Reasoning" ) - - # We always want to render the full crew tree when possible so the - # Live view updates coherently. Fallbacks: crew tree → branch itself. - tree_to_use = self.current_crew_tree or crew_tree or branch_to_use - - if branch_to_use is None: - # Nothing to attach to, abort - return None - - # Reuse existing reasoning branch if present - reasoning_branch = self.current_reasoning_branch - if reasoning_branch is None: - reasoning_branch = branch_to_use.add("") - self.current_reasoning_branch = reasoning_branch - - # Build label text depending on attempt - status_text = ( - f"Reasoning (Attempt {attempt})" if attempt > 1 else "Reasoning..." - ) - self.update_tree_label(reasoning_branch, "🧠", status_text, "blue") - - self.print(tree_to_use) - self.print() - - return reasoning_branch + self.print_panel(content, panel_title, "blue") def handle_reasoning_completed( self, plan: str, ready: bool, - crew_tree: Tree | None, ) -> None: - """Handle agent reasoning completed event.""" + """Handle agent reasoning completed event with panel display.""" if not self.verbose: return - reasoning_branch = self.current_reasoning_branch - tree_to_use = ( - self.current_crew_tree - or self.current_lite_agent_branch - or self.current_task_branch - or crew_tree - ) - style = "green" if ready else "yellow" - status_text = ( - "Reasoning Completed" if ready else "Reasoning Completed (Not Ready)" - ) + status_text = "Ready" if ready else "Not Ready" - if reasoning_branch is not None: - self.update_tree_label(reasoning_branch, "✅", status_text, style) + content = Text() + content.append("Reasoning Completed\n", style=f"{style} bold") + content.append("Status: ", style="white") + content.append(f"{status_text}\n", style=style) - if tree_to_use is not None: - self.print(tree_to_use) - - # Show plan in a panel (trim very long plans) if plan: - plan_panel = Panel( - Text(plan, style="white"), - title="🧠 Reasoning Plan", - border_style=style, - padding=(1, 2), - ) - self.print(plan_panel) + plan_preview = plan[:500] + "..." if len(plan) > 500 else plan + content.append("Plan: ", style="white") + content.append(f"{plan_preview}\n", style=style) - self.print() - - # Clear stored branch after completion - self.current_reasoning_branch = None + self.print_panel(content, "✅ Reasoning Complete", style) def handle_reasoning_failed( self, error: str, - crew_tree: Tree | None, ) -> None: - """Handle agent reasoning failure event.""" + """Handle agent reasoning failure event with panel display.""" if not self.verbose: return - reasoning_branch = self.current_reasoning_branch - tree_to_use = ( - self.current_crew_tree - or self.current_lite_agent_branch - or self.current_task_branch - or crew_tree - ) - - if reasoning_branch is not None: - self.update_tree_label(reasoning_branch, "❌", "Reasoning Failed", "red") - - if tree_to_use is not None: - self.print(tree_to_use) - - # Error panel error_content = self.create_status_content( "Reasoning Failed", "Error", "red", Error=error, ) - self.print_panel(error_content, "Reasoning Error", "red") - - # Clear stored branch after failure - self.current_reasoning_branch = None + self.print_panel(error_content, "❌ Reasoning Error", "red") # ----------- AGENT LOGGING EVENTS ----------- @@ -1545,74 +831,12 @@ To enable tracing, do any one of these: return import json - import re from crewai.agents.parser import AgentAction, AgentFinish agent_role = agent_role.partition("\n")[0] if isinstance(formatted_answer, AgentAction): - thought = re.sub(r"\n+", "\n", formatted_answer.thought) - formatted_json = json.dumps( - json.loads(formatted_answer.tool_input), - indent=2, - ensure_ascii=False, - ) - - # Create content for the action panel - content = Text() - content.append("Agent: ", style="white") - content.append(f"{agent_role}\n\n", style="bright_green bold") - - if thought and thought != "": - content.append("Thought: ", style="white") - content.append(f"{thought}\n\n", style="bright_green") - - content.append("Using Tool: ", style="white") - content.append(f"{formatted_answer.tool}\n\n", style="bright_green bold") - - content.append("Tool Input:\n", style="white") - - # Create a syntax-highlighted JSON code block - json_syntax = Syntax( - formatted_json, - "json", - theme="monokai", - line_numbers=False, - background_color="default", - word_wrap=True, - ) - - content.append("\n") - - # Create separate panels for better organization - main_content = Text() - main_content.append("Agent: ", style="white") - main_content.append(f"{agent_role}\n\n", style="bright_green bold") - - if thought and thought != "": - main_content.append("Thought: ", style="white") - main_content.append(f"{thought}\n\n", style="bright_green") - - main_content.append("Using Tool: ", style="white") - main_content.append(f"{formatted_answer.tool}", style="bright_green bold") - - # Create the main action panel - action_panel = Panel( - main_content, - title="🔧 Agent Tool Execution", - border_style="magenta", - padding=(1, 2), - ) - - # Create the JSON input panel - input_panel = Panel( - json_syntax, - title="Tool Input", - border_style="blue", - padding=(1, 2), - ) - # Create tool output content with better formatting output_text = str(formatted_answer.result) if len(output_text) > 2000: @@ -1626,8 +850,6 @@ To enable tracing, do any one of these: ) # Print all panels - self.print(action_panel) - self.print(input_panel) self.print(output_panel) self.print() @@ -1668,246 +890,111 @@ To enable tracing, do any one of these: self.print(finish_panel) self.print() - def handle_memory_retrieval_started( - self, - agent_branch: Tree | None, - crew_tree: Tree | None, - ) -> Tree | None: + def handle_memory_retrieval_started(self) -> None: + """Handle memory retrieval started event with panel display.""" if not self.verbose: - return None + return - branch_to_use = agent_branch or self.current_lite_agent_branch - tree_to_use = branch_to_use or crew_tree + content = Text() + content.append("Memory Retrieval Started\n", style="blue bold") + content.append("Status: ", style="white") + content.append("Retrieving...\n", style="blue") - if branch_to_use is None or tree_to_use is None: - if crew_tree is not None: - branch_to_use = tree_to_use = crew_tree - else: - return None - - memory_branch = branch_to_use.add("") - self.update_tree_label(memory_branch, "🧠", "Memory Retrieval Started", "blue") - - self.print(tree_to_use) - self.print() - return memory_branch + self.print_panel(content, "🧠 Memory Retrieval", "blue") def handle_memory_retrieval_completed( self, - agent_branch: Tree | None, - crew_tree: Tree | None, memory_content: str, retrieval_time_ms: float, ) -> None: + """Handle memory retrieval completed event with panel display.""" if not self.verbose: return - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree - - if branch_to_use is None and tree_to_use is not None: - branch_to_use = tree_to_use - - def add_panel() -> None: - memory_text = str(memory_content) - if len(memory_text) > 500: - memory_text = memory_text[:497] + "..." - - memory_panel = Panel( - Text(memory_text, style="white"), - title="🧠 Retrieved Memory", - subtitle=f"Retrieval Time: {retrieval_time_ms:.2f}ms", - border_style="green", - padding=(1, 2), - ) - self.print(memory_panel) - self.print() - - if branch_to_use is None or tree_to_use is None: - add_panel() - return - - memory_branch_found = False - for child in branch_to_use.children: - if "Memory Retrieval Started" in str(child.label): - self.update_tree_label( - child, "✅", "Memory Retrieval Completed", "green" - ) - memory_branch_found = True - break - - if not memory_branch_found: - for child in branch_to_use.children: - if ( - "Memory Retrieval" in str(child.label) - and "Started" not in str(child.label) - and "Completed" not in str(child.label) - ): - self.update_tree_label( - child, "✅", "Memory Retrieval Completed", "green" - ) - memory_branch_found = True - break - - if not memory_branch_found: - memory_branch = branch_to_use.add("") - self.update_tree_label( - memory_branch, "✅", "Memory Retrieval Completed", "green" - ) - - self.print(tree_to_use) + content = Text() + content.append("Memory Retrieval Completed\n", style="green bold") + content.append("Time: ", style="white") + content.append(f"{retrieval_time_ms:.2f}ms\n", style="green") if memory_content: - add_panel() + memory_text = str(memory_content) - def handle_memory_query_completed( - self, - agent_branch: Tree | None, - source_type: str, - query_time_ms: float, - crew_tree: Tree | None, - ) -> None: - if not self.verbose: - return + content.append("Content: \n", style="white") + content.append(f"{memory_text}\n", style="green ") - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree - - if branch_to_use is None and tree_to_use is not None: - branch_to_use = tree_to_use - - if branch_to_use is None: - return - - memory_type = source_type.replace("_", " ").title() - - for child in branch_to_use.children: - if "Memory Retrieval" in str(child.label): - for inner_child in child.children: - sources_branch = inner_child - if "Sources Used" in str(inner_child.label): - sources_branch.add(f"✅ {memory_type} ({query_time_ms:.2f}ms)") - break - else: - sources_branch = child.add("Sources Used") - sources_branch.add(f"✅ {memory_type} ({query_time_ms:.2f}ms)") - break + self.print_panel(content, "🧠 Memory Retrieved", "green") def handle_memory_query_failed( self, - agent_branch: Tree | None, - crew_tree: Tree | None, error: str, source_type: str, ) -> None: + """Handle memory query failed event with panel display.""" if not self.verbose: return - branch_to_use = self.current_lite_agent_branch or agent_branch - tree_to_use = branch_to_use or crew_tree - - if branch_to_use is None and tree_to_use is not None: - branch_to_use = tree_to_use - - if branch_to_use is None: - return - memory_type = source_type.replace("_", " ").title() - for child in branch_to_use.children: - if "Memory Retrieval" in str(child.label): - for inner_child in child.children: - sources_branch = inner_child - if "Sources Used" in str(inner_child.label): - sources_branch.add(f"❌ {memory_type} - Error: {error}") - break - else: - sources_branch = child.add("🧠 Sources Used") - sources_branch.add(f"❌ {memory_type} - Error: {error}") - break + content = Text() + content.append("Memory Query Failed\n", style="red bold") + content.append("Source: ", style="white") + content.append(f"{memory_type}\n", style="red") + content.append("Error: ", style="white") + content.append(f"{error}\n", style="red") - def handle_memory_save_started( - self, agent_branch: Tree | None, crew_tree: Tree | None - ) -> None: + self.print_panel(content, "❌ Memory Query Error", "red") + + def handle_memory_save_started(self) -> None: + """Handle memory save started event with panel display.""" if not self.verbose: return - branch_to_use = agent_branch or self.current_lite_agent_branch - tree_to_use = branch_to_use or crew_tree + content = Text() + content.append("Memory Save Started\n", style="blue bold") + content.append("Status: ", style="white") + content.append("Saving...\n", style="blue") - if tree_to_use is None: - return - - for child in tree_to_use.children: - if "Memory Update" in str(child.label): - break - else: - memory_branch = tree_to_use.add("") - self.update_tree_label( - memory_branch, "🧠", "Memory Update Overall", "white" - ) - - self.print(tree_to_use) - self.print() + self.print_panel(content, "🧠 Memory Save", "blue") def handle_memory_save_completed( self, - agent_branch: Tree | None, - crew_tree: Tree | None, save_time_ms: float, source_type: str, ) -> None: + """Handle memory save completed event with panel display.""" if not self.verbose: return - branch_to_use = agent_branch or self.current_lite_agent_branch - tree_to_use = branch_to_use or crew_tree - - if tree_to_use is None: - return - memory_type = source_type.replace("_", " ").title() - content = f"✅ {memory_type} Memory Saved ({save_time_ms:.2f}ms)" - for child in tree_to_use.children: - if "Memory Update" in str(child.label): - child.add(content) - break - else: - memory_branch = tree_to_use.add("") - memory_branch.add(content) + content = Text() + content.append("Memory Save Completed\n", style="green bold") + content.append("Source: ", style="white") + content.append(f"{memory_type}\n", style="green") + content.append("Time: ", style="white") + content.append(f"{save_time_ms:.2f}ms\n", style="green") - self.print(tree_to_use) - self.print() + self.print_panel(content, "✅ Memory Saved", "green") def handle_memory_save_failed( self, - agent_branch: Tree | None, error: str, source_type: str, - crew_tree: Tree | None, ) -> None: + """Handle memory save failed event with panel display.""" if not self.verbose: return - branch_to_use = agent_branch or self.current_lite_agent_branch - tree_to_use = branch_to_use or crew_tree - - if branch_to_use is None or tree_to_use is None: - return - memory_type = source_type.replace("_", " ").title() - content = f"❌ {memory_type} Memory Save Failed" - for child in branch_to_use.children: - if "Memory Update" in str(child.label): - child.add(content) - break - else: - memory_branch = branch_to_use.add("") - memory_branch.add(content) - self.print(tree_to_use) - self.print() + content = Text() + content.append("Memory Save Failed\n", style="red bold") + content.append("Source: ", style="white") + content.append(f"{memory_type}\n", style="red") + content.append("Error: ", style="white") + content.append(f"{error}\n", style="red") + + self.print_panel(content, "❌ Memory Save Error", "red") def handle_guardrail_started( self, @@ -1974,94 +1061,30 @@ To enable tracing, do any one of these: agent_id: str, is_multiturn: bool = False, turn_number: int = 1, - ) -> Tree | None: - """Handle A2A delegation started event. - - Args: - endpoint: A2A agent endpoint URL - task_description: Task being delegated - agent_id: A2A agent identifier - is_multiturn: Whether this is part of a multiturn conversation - turn_number: Current turn number in conversation (1-indexed) - """ - branch_to_use = self.current_lite_agent_branch or self.current_task_branch - tree_to_use = self.current_crew_tree or branch_to_use - a2a_branch: Tree | None = None - + ) -> None: + """Handle A2A delegation started event with panel display.""" if is_multiturn: - if self.current_a2a_turn_count == 0 and not isinstance( - self.current_a2a_conversation_branch, Tree - ): - if branch_to_use is not None and tree_to_use is not None: - self.current_a2a_conversation_branch = branch_to_use.add("") - self.update_tree_label( - self.current_a2a_conversation_branch, - "💬", - f"Multiturn A2A Conversation ({agent_id})", - "cyan", - ) - self.print(tree_to_use) - self.print() - else: - self.current_a2a_conversation_branch = "MULTITURN_NO_TREE" - - content = Text() - content.append( - "Multiturn A2A Conversation Started\n\n", style="cyan bold" - ) - content.append("Agent ID: ", style="white") - content.append(f"{agent_id}\n", style="cyan") - content.append("Note: ", style="white dim") - content.append( - "Conversation will be tracked in tree view", style="cyan dim" - ) - - panel = self.create_panel( - content, "💬 Multiturn Conversation", "cyan" - ) - self.print(panel) - self.print() - self.current_a2a_turn_count = turn_number - return ( - self.current_a2a_conversation_branch - if isinstance(self.current_a2a_conversation_branch, Tree) - else None - ) - - if branch_to_use is not None and tree_to_use is not None: - a2a_branch = branch_to_use.add("") - self.update_tree_label( - a2a_branch, - "🔗", - f"Delegating to A2A Agent ({agent_id})", - "cyan", - ) - - self.print(tree_to_use) - self.print() - content = Text() - content.append("A2A Delegation Started\n\n", style="cyan bold") + content.append("A2A Delegation Started\n", style="cyan bold") content.append("Agent ID: ", style="white") content.append(f"{agent_id}\n", style="cyan") content.append("Endpoint: ", style="white") - content.append(f"{endpoint}\n\n", style="cyan dim") - content.append("Task Description:\n", style="white") - + content.append(f"{endpoint}\n", style="cyan") + if is_multiturn: + content.append("Turn: ", style="white") + content.append(f"{turn_number}\n", style="cyan") + content.append("Task: ", style="white") task_preview = ( - task_description - if len(task_description) <= 200 - else task_description[:197] + "..." + task_description[:200] + "..." + if len(task_description) > 200 + else task_description ) - content.append(task_preview, style="cyan") + content.append(f"{task_preview}\n", style="cyan") - panel = self.create_panel(content, "🔗 A2A Delegation", "cyan") - self.print(panel) - self.print() - - return a2a_branch + self.print_panel(content, "🔗 A2A Delegation", "cyan") + return def handle_a2a_delegation_completed( self, @@ -2070,154 +1093,58 @@ To enable tracing, do any one of these: error: str | None = None, is_multiturn: bool = False, ) -> None: - """Handle A2A delegation completed event. - - Args: - status: Completion status - result: Optional result message - error: Optional error message (or response for input_required) - is_multiturn: Whether this is part of a multiturn conversation - """ - tree_to_use = self.current_crew_tree or self.current_task_branch - a2a_branch = None - - if is_multiturn and self.current_a2a_conversation_branch: - has_tree = isinstance(self.current_a2a_conversation_branch, Tree) - - if status == "input_required" and error: - pass - elif status == "completed": - if has_tree and isinstance(self.current_a2a_conversation_branch, Tree): - final_turn = self.current_a2a_conversation_branch.add("") - self.update_tree_label( - final_turn, - "✅", - "Conversation Completed", - "green", - ) - - if tree_to_use: - self.print(tree_to_use) - self.print() - - self.current_a2a_conversation_branch = None + """Handle A2A delegation completed event with panel display.""" + if is_multiturn: + if status in ["completed", "failed"]: self.current_a2a_turn_count = 0 - elif status == "failed": - if has_tree and isinstance(self.current_a2a_conversation_branch, Tree): - error_turn = self.current_a2a_conversation_branch.add("") - error_msg = ( - error[:150] + "..." if error and len(error) > 150 else error - ) - self.update_tree_label( - error_turn, - "❌", - f"Failed: {error_msg}" if error else "Conversation Failed", - "red", - ) - - if tree_to_use: - self.print(tree_to_use) - self.print() - - self.current_a2a_conversation_branch = None - self.current_a2a_turn_count = 0 - - return - - if a2a_branch and tree_to_use: - if status == "completed": - self.update_tree_label( - a2a_branch, - "✅", - "A2A Delegation Completed", - "green", - ) - elif status == "failed": - self.update_tree_label( - a2a_branch, - "❌", - "A2A Delegation Failed", - "red", - ) - else: - self.update_tree_label( - a2a_branch, - "⚠️", - f"A2A Delegation {status.replace('_', ' ').title()}", - "yellow", - ) - - self.print(tree_to_use) - self.print() if status == "completed" and result: content = Text() - content.append("A2A Delegation Completed\n\n", style="green bold") - content.append("Result:\n", style="white") + content.append("A2A Delegation Completed\n", style="green bold") + content.append("Result: ", style="white") + result_preview = result[:500] + "..." if len(result) > 500 else result + content.append(f"{result_preview}\n", style="green") - result_preview = result if len(result) <= 500 else result[:497] + "..." - content.append(result_preview, style="green") - - panel = self.create_panel(content, "✅ A2A Success", "green") - self.print(panel) - self.print() + self.print_panel(content, "✅ A2A Success", "green") elif status == "input_required" and error: content = Text() - content.append("A2A Response\n\n", style="cyan bold") - content.append("Message:\n", style="white") + content.append("A2A Response Received\n", style="cyan bold") + content.append("Message: ", style="white") + response_preview = error[:500] + "..." if len(error) > 500 else error + content.append(f"{response_preview}\n", style="cyan") - response_preview = error if len(error) <= 500 else error[:497] + "..." - content.append(response_preview, style="cyan") - - panel = self.create_panel(content, "💬 A2A Response", "cyan") - self.print(panel) - self.print() - elif error: + self.print_panel(content, "💬 A2A Response", "cyan") + elif status == "failed": content = Text() - content.append( - "A2A Delegation Issue\n\n", - style="red bold" if status == "failed" else "yellow bold", - ) - content.append("Status: ", style="white") - content.append( - f"{status}\n\n", style="red" if status == "failed" else "yellow" - ) - content.append("Message:\n", style="white") - content.append(error, style="red" if status == "failed" else "yellow") + content.append("A2A Delegation Failed\n", style="red bold") + if error: + content.append("Error: ", style="white") + content.append(f"{error}\n", style="red") - panel_style = "red" if status == "failed" else "yellow" - panel_title = "❌ A2A Failed" if status == "failed" else "⚠️ A2A Status" - panel = self.create_panel(content, panel_title, panel_style) - self.print(panel) - self.print() + self.print_panel(content, "❌ A2A Failed", "red") + else: + content = Text() + content.append(f"A2A Delegation {status.title()}\n", style="yellow bold") + if error: + content.append("Message: ", style="white") + content.append(f"{error}\n", style="yellow") + + self.print_panel(content, "⚠️ A2A Status", "yellow") def handle_a2a_conversation_started( self, agent_id: str, endpoint: str, ) -> None: - """Handle A2A conversation started event. + """Handle A2A conversation started event with panel display.""" + content = Text() + content.append("A2A Conversation Started\n", style="cyan bold") + content.append("Agent ID: ", style="white") + content.append(f"{agent_id}\n", style="cyan") + content.append("Endpoint: ", style="white") + content.append(f"{endpoint}\n", style="cyan ") - Args: - agent_id: A2A agent identifier - endpoint: A2A agent endpoint URL - """ - branch_to_use = self.current_lite_agent_branch or self.current_task_branch - tree_to_use = self.current_crew_tree or branch_to_use - - if not isinstance(self.current_a2a_conversation_branch, Tree): - if branch_to_use is not None and tree_to_use is not None: - self.current_a2a_conversation_branch = branch_to_use.add("") - self.update_tree_label( - self.current_a2a_conversation_branch, - "💬", - f"Multiturn A2A Conversation ({agent_id})", - "cyan", - ) - self.print(tree_to_use) - self.print() - else: - self.current_a2a_conversation_branch = "MULTITURN_NO_TREE" + self.print_panel(content, "💬 A2A Conversation", "cyan") def handle_a2a_message_sent( self, @@ -2225,13 +1152,7 @@ To enable tracing, do any one of these: turn_number: int, agent_role: str | None = None, ) -> None: - """Handle A2A message sent event. - - Args: - message: Message content sent to the A2A agent - turn_number: Current turn number - agent_role: Role of the CrewAI agent sending the message - """ + """Handle A2A message sent event - store for display with response.""" self._pending_a2a_message = message self._pending_a2a_agent_role = agent_role self._pending_a2a_turn_number = turn_number @@ -2243,91 +1164,56 @@ To enable tracing, do any one of these: status: str, agent_role: str | None = None, ) -> None: - """Handle A2A response received event. + """Handle A2A response received event with panel display.""" + crewai_agent_role = self._pending_a2a_agent_role or agent_role or "User" + message_content = self._pending_a2a_message or "" - Args: - response: Response content from the A2A agent - turn_number: Current turn number - status: Response status (input_required, completed, etc.) - agent_role: Role of the CrewAI agent (for display) - """ - if self.current_a2a_conversation_branch and isinstance( - self.current_a2a_conversation_branch, Tree - ): - if turn_number in self._a2a_turn_branches: - turn_branch = self._a2a_turn_branches[turn_number] - else: - turn_branch = self.current_a2a_conversation_branch.add("") - self.update_tree_label( - turn_branch, - "💬", - f"Turn {turn_number}", - "cyan", - ) - self._a2a_turn_branches[turn_number] = turn_branch + # Determine status styling + if status == "completed": + style = "green" + status_indicator = "✓" + elif status == "input_required": + style = "yellow" + status_indicator = "❓" + elif status == "failed": + style = "red" + status_indicator = "✗" + elif status == "auth_required": + style = "magenta" + status_indicator = "🔒" + elif status == "canceled": + style = "" + status_indicator = "⊘" + else: + style = "cyan" + status_indicator = "" - crewai_agent_role = self._pending_a2a_agent_role or agent_role or "User" - message_content = self._pending_a2a_message or "sent message" + content = Text() + content.append(f"A2A Turn {turn_number}\n", style="cyan bold") + content.append("Status: ", style="white") + content.append(f"{status_indicator} {status}\n", style=style) - message_preview = ( - message_content[:100] + "..." - if len(message_content) > 100 + if message_content: + content.append(f"{crewai_agent_role}: ", style="blue bold") + msg_preview = ( + message_content[:200] + "..." + if len(message_content) > 200 else message_content ) + content.append(f"{msg_preview}\n", style="blue") - user_node = turn_branch.add("") - self.update_tree_label( - user_node, - f"{crewai_agent_role} 👤 : ", - f'"{message_preview}"', - "blue", - ) + content.append( + f"{self._current_a2a_agent_name or 'A2A Agent'}: ", style=f"{style} bold" + ) + response_preview = response[:200] + "..." if len(response) > 200 else response + content.append(f"{response_preview}\n", style=style) - agent_node = turn_branch.add("") - response_preview = ( - response[:100] + "..." if len(response) > 100 else response - ) + self.print_panel(content, f"💬 A2A Turn #{turn_number}", style) - a2a_agent_display = f"{self._current_a2a_agent_name} \U0001f916: " - - if status == "completed": - response_color = "green" - status_indicator = "✓" - elif status == "input_required": - response_color = "yellow" - status_indicator = "❓" - elif status == "failed": - response_color = "red" - status_indicator = "✗" - elif status == "auth_required": - response_color = "magenta" - status_indicator = "🔒" - elif status == "canceled": - response_color = "dim" - status_indicator = "⊘" - else: - response_color = "cyan" - status_indicator = "" - - label = f'"{response_preview}"' - if status_indicator: - label = f"{status_indicator} {label}" - - self.update_tree_label( - agent_node, - a2a_agent_display, - label, - response_color, - ) - - self._pending_a2a_message = None - self._pending_a2a_agent_role = None - self._pending_a2a_turn_number = None - - tree_to_use = self.current_crew_tree or self.current_task_branch - if tree_to_use: - self.print(tree_to_use) - self.print() + # Clear pending state + self._pending_a2a_message = None + self._pending_a2a_agent_role = None + self._pending_a2a_turn_number = None def handle_a2a_conversation_completed( self, @@ -2336,68 +1222,38 @@ To enable tracing, do any one of these: error: str | None, total_turns: int, ) -> None: - """Handle A2A conversation completed event. - - Args: - status: Final status (completed, failed, etc.) - final_result: Final result if completed successfully - error: Error message if failed - total_turns: Total number of turns in the conversation - """ - if self.current_a2a_conversation_branch and isinstance( - self.current_a2a_conversation_branch, Tree - ): - if status == "completed": - if self._pending_a2a_message and self._pending_a2a_agent_role: - if total_turns in self._a2a_turn_branches: - turn_branch = self._a2a_turn_branches[total_turns] - else: - turn_branch = self.current_a2a_conversation_branch.add("") - self.update_tree_label( - turn_branch, - "💬", - f"Turn {total_turns}", - "cyan", - ) - self._a2a_turn_branches[total_turns] = turn_branch - - crewai_agent_role = self._pending_a2a_agent_role - message_content = self._pending_a2a_message - - message_preview = ( - message_content[:100] + "..." - if len(message_content) > 100 - else message_content - ) - - user_node = turn_branch.add("") - self.update_tree_label( - user_node, - f"{crewai_agent_role} 👤 : ", - f'"{message_preview}"', - "green", - ) - - self._pending_a2a_message = None - self._pending_a2a_agent_role = None - self._pending_a2a_turn_number = None - elif status == "failed": - error_turn = self.current_a2a_conversation_branch.add("") - error_msg = error[:150] + "..." if error and len(error) > 150 else error - self.update_tree_label( - error_turn, - "❌", - f"Failed: {error_msg}" if error else "Conversation Failed", - "red", + """Handle A2A conversation completed event with panel display.""" + if status == "completed": + content = Text() + content.append("A2A Conversation Completed\n", style="green bold") + content.append("Total Turns: ", style="white") + content.append(f"{total_turns}\n", style="green") + if final_result: + content.append("Result: ", style="white") + result_preview = ( + final_result[:500] + "..." + if len(final_result) > 500 + else final_result ) + content.append(f"{result_preview}\n", style="green") - tree_to_use = self.current_crew_tree or self.current_task_branch - if tree_to_use: - self.print(tree_to_use) - self.print() + self.print_panel(content, "✅ A2A Complete", "green") + elif status == "failed": + content = Text() + content.append("A2A Conversation Failed\n", style="red bold") + content.append("Total Turns: ", style="white") + content.append(f"{total_turns}\n", style="red") + if error: + content.append("Error: ", style="white") + content.append(f"{error}\n", style="red") - self.current_a2a_conversation_branch = None + self.print_panel(content, "❌ A2A Failed", "red") + + # Reset state self.current_a2a_turn_count = 0 + self._pending_a2a_message = None + self._pending_a2a_agent_role = None + self._pending_a2a_turn_number = None # ----------- MCP EVENTS ----------- @@ -2416,12 +1272,10 @@ To enable tracing, do any one of these: content = Text() reconnect_text = " (Reconnecting)" if is_reconnect else "" content.append(f"MCP Connection Started{reconnect_text}\n\n", style="cyan bold") - content.append("Server: ", style="white") - content.append(f"{server_name}\n", style="cyan") if server_url: content.append("URL: ", style="white") - content.append(f"{server_url}\n", style="cyan dim") + content.append(f"{server_url}\n", style="cyan ") if transport_type: content.append("Transport: ", style="white") @@ -2457,7 +1311,7 @@ To enable tracing, do any one of these: if server_url: content.append("URL: ", style="white") - content.append(f"{server_url}\n", style="green dim") + content.append(f"{server_url}\n", style="green ") if transport_type: content.append("Transport: ", style="white") @@ -2490,7 +1344,7 @@ To enable tracing, do any one of these: if server_url: content.append("URL: ", style="white") - content.append(f"{server_url}\n", style="red dim") + content.append(f"{server_url}\n", style="red ") if transport_type: content.append("Transport: ", style="white") @@ -2520,49 +1374,14 @@ To enable tracing, do any one of these: return content = self.create_status_content( - "MCP Tool Execution Started", + "MCP Tool Started", tool_name, "yellow", tool_args=tool_args or {}, Server=server_name, ) - panel = self.create_panel(content, "🔧 MCP Tool", "yellow") - self.print(panel) - self.print() - - def handle_mcp_tool_execution_completed( - self, - server_name: str, - tool_name: str, - tool_args: dict[str, Any] | None = None, - result: Any | None = None, - execution_duration_ms: float | None = None, - ) -> None: - """Handle MCP tool execution completed event.""" - if not self.verbose: - return - - content = self.create_status_content( - "MCP Tool Execution Completed", - tool_name, - "green", - tool_args=tool_args or {}, - Server=server_name, - ) - - if execution_duration_ms is not None: - content.append("Duration: ", style="white") - content.append(f"{execution_duration_ms:.2f}ms\n", style="green") - - if result is not None: - result_str = str(result) - if len(result_str) > 500: - result_str = result_str[:497] + "..." - content.append("\nResult: ", style="white bold") - content.append(f"{result_str}\n", style="green") - - panel = self.create_panel(content, "✅ MCP Tool Completed", "green") + panel = self.create_panel(content, "🔧 MCP Tool Started", "yellow") self.print(panel) self.print() diff --git a/lib/crewai/src/crewai/tools/tool_usage.py b/lib/crewai/src/crewai/tools/tool_usage.py index 8f753f412..ab3d0fc25 100644 --- a/lib/crewai/src/crewai/tools/tool_usage.py +++ b/lib/crewai/src/crewai/tools/tool_usage.py @@ -249,6 +249,7 @@ class ToolUsage: "tool_args": self.action.tool_input, "tool_class": self.action.tool, "agent": self.agent, + "run_attempts": self._run_attempts, } if self.agent.fingerprint: # type: ignore @@ -435,6 +436,7 @@ class ToolUsage: "tool_args": self.action.tool_input, "tool_class": self.action.tool, "agent": self.agent, + "run_attempts": self._run_attempts, } # TODO: Investigate fingerprint attribute availability on BaseAgent/LiteAgent diff --git a/lib/crewai/tests/test_flow_human_input_integration.py b/lib/crewai/tests/test_flow_human_input_integration.py index 63f6308ed..e60cfe514 100644 --- a/lib/crewai/tests/test_flow_human_input_integration.py +++ b/lib/crewai/tests/test_flow_human_input_integration.py @@ -7,22 +7,19 @@ from crewai.events.event_listener import event_listener class TestFlowHumanInputIntegration: """Test integration between Flow execution and human input functionality.""" - def test_console_formatter_pause_resume_methods(self): - """Test that ConsoleFormatter pause/resume methods work correctly.""" + def test_console_formatter_pause_resume_methods_exist(self): + """Test that ConsoleFormatter pause/resume methods exist and are callable.""" formatter = event_listener.formatter - original_paused_state = formatter._live_paused + # Methods should exist and be callable + assert hasattr(formatter, "pause_live_updates") + assert hasattr(formatter, "resume_live_updates") + assert callable(formatter.pause_live_updates) + assert callable(formatter.resume_live_updates) - try: - formatter._live_paused = False - - formatter.pause_live_updates() - assert formatter._live_paused - - formatter.resume_live_updates() - assert not formatter._live_paused - finally: - formatter._live_paused = original_paused_state + # Should not raise + formatter.pause_live_updates() + formatter.resume_live_updates() @patch("builtins.input", return_value="") def test_human_input_pauses_flow_updates(self, mock_input): @@ -38,23 +35,16 @@ class TestFlowHumanInputIntegration: formatter = event_listener.formatter - original_paused_state = formatter._live_paused + with ( + patch.object(formatter, "pause_live_updates") as mock_pause, + patch.object(formatter, "resume_live_updates") as mock_resume, + ): + result = executor._ask_human_input("Test result") - try: - formatter._live_paused = False - - with ( - patch.object(formatter, "pause_live_updates") as mock_pause, - patch.object(formatter, "resume_live_updates") as mock_resume, - ): - result = executor._ask_human_input("Test result") - - mock_pause.assert_called_once() - mock_resume.assert_called_once() - mock_input.assert_called_once() - assert result == "" - finally: - formatter._live_paused = original_paused_state + mock_pause.assert_called_once() + mock_resume.assert_called_once() + mock_input.assert_called_once() + assert result == "" @patch("builtins.input", side_effect=["feedback", ""]) def test_multiple_human_input_rounds(self, mock_input): @@ -70,53 +60,46 @@ class TestFlowHumanInputIntegration: formatter = event_listener.formatter - original_paused_state = formatter._live_paused + pause_calls = [] + resume_calls = [] - try: - pause_calls = [] - resume_calls = [] + def track_pause(): + pause_calls.append(True) - def track_pause(): - pause_calls.append(True) + def track_resume(): + resume_calls.append(True) - def track_resume(): - resume_calls.append(True) + with ( + patch.object(formatter, "pause_live_updates", side_effect=track_pause), + patch.object( + formatter, "resume_live_updates", side_effect=track_resume + ), + ): + result1 = executor._ask_human_input("Test result 1") + assert result1 == "feedback" - with ( - patch.object(formatter, "pause_live_updates", side_effect=track_pause), - patch.object( - formatter, "resume_live_updates", side_effect=track_resume - ), - ): - result1 = executor._ask_human_input("Test result 1") - assert result1 == "feedback" + result2 = executor._ask_human_input("Test result 2") + assert result2 == "" - result2 = executor._ask_human_input("Test result 2") - assert result2 == "" - - assert len(pause_calls) == 2 - assert len(resume_calls) == 2 - finally: - formatter._live_paused = original_paused_state + assert len(pause_calls) == 2 + assert len(resume_calls) == 2 def test_pause_resume_with_no_live_session(self): """Test pause/resume methods handle case when no Live session exists.""" formatter = event_listener.formatter - original_live = formatter._live - original_paused_state = formatter._live_paused + original_streaming_live = formatter._streaming_live try: - formatter._live = None - formatter._live_paused = False + formatter._streaming_live = None + # Should not raise when no session exists formatter.pause_live_updates() formatter.resume_live_updates() - assert not formatter._live_paused + assert formatter._streaming_live is None finally: - formatter._live = original_live - formatter._live_paused = original_paused_state + formatter._streaming_live = original_streaming_live def test_pause_resume_exception_handling(self): """Test that resume is called even if exception occurs during human input.""" @@ -131,23 +114,18 @@ class TestFlowHumanInputIntegration: formatter = event_listener.formatter - original_paused_state = formatter._live_paused + with ( + patch.object(formatter, "pause_live_updates") as mock_pause, + patch.object(formatter, "resume_live_updates") as mock_resume, + patch( + "builtins.input", side_effect=KeyboardInterrupt("Test exception") + ), + ): + with pytest.raises(KeyboardInterrupt): + executor._ask_human_input("Test result") - try: - with ( - patch.object(formatter, "pause_live_updates") as mock_pause, - patch.object(formatter, "resume_live_updates") as mock_resume, - patch( - "builtins.input", side_effect=KeyboardInterrupt("Test exception") - ), - ): - with pytest.raises(KeyboardInterrupt): - executor._ask_human_input("Test result") - - mock_pause.assert_called_once() - mock_resume.assert_called_once() - finally: - formatter._live_paused = original_paused_state + mock_pause.assert_called_once() + mock_resume.assert_called_once() def test_training_mode_human_input(self): """Test human input in training mode.""" @@ -162,28 +140,25 @@ class TestFlowHumanInputIntegration: formatter = event_listener.formatter - original_paused_state = formatter._live_paused + with ( + patch.object(formatter, "pause_live_updates") as mock_pause, + patch.object(formatter, "resume_live_updates") as mock_resume, + patch.object(formatter.console, "print") as mock_console_print, + patch("builtins.input", return_value="training feedback"), + ): + result = executor._ask_human_input("Test result") - try: - with ( - patch.object(formatter, "pause_live_updates") as mock_pause, - patch.object(formatter, "resume_live_updates") as mock_resume, - patch("builtins.input", return_value="training feedback"), - ): - result = executor._ask_human_input("Test result") + mock_pause.assert_called_once() + mock_resume.assert_called_once() + assert result == "training feedback" - mock_pause.assert_called_once() - mock_resume.assert_called_once() - assert result == "training feedback" - - executor._printer.print.assert_called() - call_args = [ - call[1]["content"] - for call in executor._printer.print.call_args_list - ] - training_prompt_found = any( - "TRAINING MODE" in content for content in call_args - ) - assert training_prompt_found - finally: - formatter._live_paused = original_paused_state + # Verify the training panel was printed via formatter's console + mock_console_print.assert_called() + # Check that a Panel with training title was printed + call_args = mock_console_print.call_args_list + training_panel_found = any( + hasattr(call[0][0], "title") and "Training" in str(call[0][0].title) + for call in call_args + if call[0] + ) + assert training_panel_found diff --git a/lib/crewai/tests/utilities/test_console_formatter_pause_resume.py b/lib/crewai/tests/utilities/test_console_formatter_pause_resume.py index 6a64852e1..0964a0756 100644 --- a/lib/crewai/tests/utilities/test_console_formatter_pause_resume.py +++ b/lib/crewai/tests/utilities/test_console_formatter_pause_resume.py @@ -1,116 +1,107 @@ from unittest.mock import MagicMock, patch -from rich.tree import Tree from rich.live import Live from crewai.events.utils.console_formatter import ConsoleFormatter class TestConsoleFormatterPauseResume: - """Test ConsoleFormatter pause/resume functionality.""" + """Test ConsoleFormatter pause/resume functionality for HITL features.""" - def test_pause_live_updates_with_active_session(self): - """Test pausing when Live session is active.""" + def test_pause_stops_active_streaming_session(self): + """Test pausing stops an active streaming Live session.""" formatter = ConsoleFormatter() mock_live = MagicMock(spec=Live) - formatter._live = mock_live - formatter._live_paused = False + formatter._streaming_live = mock_live formatter.pause_live_updates() mock_live.stop.assert_called_once() - assert formatter._live_paused + assert formatter._streaming_live is None - def test_pause_live_updates_when_already_paused(self): - """Test pausing when already paused does nothing.""" + def test_pause_is_safe_when_no_session(self): + """Test pausing when no streaming session exists doesn't error.""" + formatter = ConsoleFormatter() + formatter._streaming_live = None + + # Should not raise + formatter.pause_live_updates() + + assert formatter._streaming_live is None + + def test_multiple_pauses_are_safe(self): + """Test calling pause multiple times is safe.""" formatter = ConsoleFormatter() mock_live = MagicMock(spec=Live) - formatter._live = mock_live - formatter._live_paused = True + formatter._streaming_live = mock_live formatter.pause_live_updates() + mock_live.stop.assert_called_once() + assert formatter._streaming_live is None - mock_live.stop.assert_not_called() - assert formatter._live_paused - - def test_pause_live_updates_with_no_session(self): - """Test pausing when no Live session exists.""" - formatter = ConsoleFormatter() - - formatter._live = None - formatter._live_paused = False - + # Second pause should not error (no session to stop) formatter.pause_live_updates() - assert formatter._live_paused - - def test_resume_live_updates_when_paused(self): - """Test resuming when paused.""" + def test_resume_is_safe(self): + """Test resume method exists and doesn't error.""" formatter = ConsoleFormatter() - formatter._live_paused = True - + # Should not raise formatter.resume_live_updates() - assert not formatter._live_paused - - def test_resume_live_updates_when_not_paused(self): - """Test resuming when not paused does nothing.""" + def test_streaming_after_pause_resume_creates_new_session(self): + """Test that streaming after pause/resume creates new Live session.""" formatter = ConsoleFormatter() + formatter.verbose = True - formatter._live_paused = False + # Simulate having an active session + mock_live = MagicMock(spec=Live) + formatter._streaming_live = mock_live + # Pause stops the session + formatter.pause_live_updates() + assert formatter._streaming_live is None + + # Resume (no-op, sessions created on demand) formatter.resume_live_updates() - assert not formatter._live_paused + # After resume, streaming should be able to start a new session + with patch("crewai.events.utils.console_formatter.Live") as mock_live_class: + mock_live_instance = MagicMock() + mock_live_class.return_value = mock_live_instance - def test_print_after_resume_restarts_live_session(self): - """Test that printing a Tree after resume creates new Live session.""" + # Simulate streaming chunk (this creates a new Live session) + formatter.handle_llm_stream_chunk("test chunk", call_type=None) + + mock_live_class.assert_called_once() + mock_live_instance.start.assert_called_once() + assert formatter._streaming_live == mock_live_instance + + def test_pause_resume_cycle_with_streaming(self): + """Test full pause/resume cycle during streaming.""" formatter = ConsoleFormatter() - - formatter._live_paused = True - formatter._live = None - - formatter.resume_live_updates() - assert not formatter._live_paused - - tree = Tree("Test") + formatter.verbose = True with patch("crewai.events.utils.console_formatter.Live") as mock_live_class: mock_live_instance = MagicMock() mock_live_class.return_value = mock_live_instance - formatter.print(tree) + # Start streaming + formatter.handle_llm_stream_chunk("chunk 1", call_type=None) + assert formatter._streaming_live == mock_live_instance - mock_live_class.assert_called_once() - mock_live_instance.start.assert_called_once() - assert formatter._live == mock_live_instance + # Pause should stop the session + formatter.pause_live_updates() + mock_live_instance.stop.assert_called_once() + assert formatter._streaming_live is None - def test_multiple_pause_resume_cycles(self): - """Test multiple pause/resume cycles work correctly.""" - formatter = ConsoleFormatter() + # Resume (no-op) + formatter.resume_live_updates() - mock_live = MagicMock(spec=Live) - formatter._live = mock_live - formatter._live_paused = False + # Create a new mock for the next session + mock_live_instance_2 = MagicMock() + mock_live_class.return_value = mock_live_instance_2 - formatter.pause_live_updates() - assert formatter._live_paused - mock_live.stop.assert_called_once() - assert formatter._live is None # Live session should be cleared - - formatter.resume_live_updates() - assert not formatter._live_paused - - formatter.pause_live_updates() - assert formatter._live_paused - - formatter.resume_live_updates() - assert not formatter._live_paused - - def test_pause_resume_state_initialization(self): - """Test that _live_paused is properly initialized.""" - formatter = ConsoleFormatter() - - assert hasattr(formatter, "_live_paused") - assert not formatter._live_paused + # Streaming again creates new session + formatter.handle_llm_stream_chunk("chunk 2", call_type=None) + assert formatter._streaming_live == mock_live_instance_2 From f3c17a249b5a2da3a917784d9762421591c1cde5 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Wed, 31 Dec 2025 14:29:42 -0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Introduce=20production-ready=20Flow?= =?UTF-8?q?s=20and=20Crews=20architecture=20with=20ne=E2=80=A6=20(#4003)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Introduce production-ready Flows and Crews architecture with new runner and updated documentation across multiple languages. * ko and pt-br for tracing missing links --------- Co-authored-by: Greyson LaLonde --- README.md | 20 +- docs/docs.json | 5 + docs/en/concepts/production-architecture.mdx | 154 +++++++++++++ docs/en/introduction.mdx | 147 ++++++------ docs/ko/concepts/production-architecture.mdx | 154 +++++++++++++ docs/ko/introduction.mdx | 152 ++++++------- docs/ko/observability/tracing.mdx | 213 ++++++++++++++++++ .../concepts/production-architecture.mdx | 154 +++++++++++++ docs/pt-BR/introduction.mdx | 149 ++++++------ docs/pt-BR/observability/tracing.mdx | 213 ++++++++++++++++++ 10 files changed, 1096 insertions(+), 265 deletions(-) create mode 100644 docs/en/concepts/production-architecture.mdx create mode 100644 docs/ko/concepts/production-architecture.mdx create mode 100644 docs/ko/observability/tracing.mdx create mode 100644 docs/pt-BR/concepts/production-architecture.mdx create mode 100644 docs/pt-BR/observability/tracing.mdx diff --git a/README.md b/README.md index 5da3e45b8..56652dd9d 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ > It empowers developers with both high-level simplicity and precise low-level control, ideal for creating autonomous AI agents tailored to any scenario. - **CrewAI Crews**: Optimize for autonomy and collaborative intelligence. -- **CrewAI Flows**: Enable granular, event-driven control, single LLM calls for precise task orchestration and supports Crews natively +- **CrewAI Flows**: The **enterprise and production architecture** for building and deploying multi-agent systems. Enable granular, event-driven control, single LLM calls for precise task orchestration and supports Crews natively With over 100,000 developers certified through our community courses at [learn.crewai.com](https://learn.crewai.com), CrewAI is rapidly becoming the standard for enterprise-ready AI automation. @@ -166,13 +166,13 @@ Ensure you have Python >=3.10 <3.14 installed on your system. CrewAI uses [UV](h First, install CrewAI: ```shell -pip install crewai +uv pip install crewai ``` If you want to install the 'crewai' package along with its optional features that include additional tools for agents, you can do so by using the following command: ```shell -pip install 'crewai[tools]' +uv pip install 'crewai[tools]' ``` The command above installs the basic package and also adds extra components which require more dependencies to function. @@ -185,14 +185,14 @@ If you encounter issues during installation or usage, here are some common solut 1. **ModuleNotFoundError: No module named 'tiktoken'** - - Install tiktoken explicitly: `pip install 'crewai[embeddings]'` - - If using embedchain or other tools: `pip install 'crewai[tools]'` + - Install tiktoken explicitly: `uv pip install 'crewai[embeddings]'` + - If using embedchain or other tools: `uv pip install 'crewai[tools]'` 2. **Failed building wheel for tiktoken** - Ensure Rust compiler is installed (see installation steps above) - For Windows: Verify Visual C++ Build Tools are installed - - Try upgrading pip: `pip install --upgrade pip` - - If issues persist, use a pre-built wheel: `pip install tiktoken --prefer-binary` + - Try upgrading pip: `uv pip install --upgrade pip` + - If issues persist, use a pre-built wheel: `uv pip install tiktoken --prefer-binary` ### 2. Setting Up Your Crew with the YAML Configuration @@ -611,7 +611,7 @@ uv build ### Installing Locally ```bash -pip install dist/*.tar.gz +uv pip install dist/*.tar.gz ``` ## Telemetry @@ -687,13 +687,13 @@ A: CrewAI is a standalone, lean, and fast Python framework built specifically fo A: Install CrewAI using pip: ```shell -pip install crewai +uv pip install crewai ``` For additional tools, use: ```shell -pip install 'crewai[tools]' +uv pip install 'crewai[tools]' ``` ### Q: Does CrewAI depend on LangChain? diff --git a/docs/docs.json b/docs/docs.json index 8c9677706..29a35f1c2 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -116,6 +116,7 @@ "en/concepts/tasks", "en/concepts/crews", "en/concepts/flows", + "en/concepts/production-architecture", "en/concepts/knowledge", "en/concepts/llms", "en/concepts/processes", @@ -559,6 +560,7 @@ "pt-BR/concepts/tasks", "pt-BR/concepts/crews", "pt-BR/concepts/flows", + "pt-BR/concepts/production-architecture", "pt-BR/concepts/knowledge", "pt-BR/concepts/llms", "pt-BR/concepts/processes", @@ -703,6 +705,7 @@ { "group": "Observabilidade", "pages": [ + "pt-BR/observability/tracing", "pt-BR/observability/overview", "pt-BR/observability/arize-phoenix", "pt-BR/observability/braintrust", @@ -984,6 +987,7 @@ "ko/concepts/tasks", "ko/concepts/crews", "ko/concepts/flows", + "ko/concepts/production-architecture", "ko/concepts/knowledge", "ko/concepts/llms", "ko/concepts/processes", @@ -1140,6 +1144,7 @@ { "group": "Observability", "pages": [ + "ko/observability/tracing", "ko/observability/overview", "ko/observability/arize-phoenix", "ko/observability/braintrust", diff --git a/docs/en/concepts/production-architecture.mdx b/docs/en/concepts/production-architecture.mdx new file mode 100644 index 000000000..ad668056f --- /dev/null +++ b/docs/en/concepts/production-architecture.mdx @@ -0,0 +1,154 @@ +--- +title: Production Architecture +description: Best practices for building production-ready AI applications with CrewAI +icon: server +mode: "wide" +--- + +# The Flow-First Mindset + +When building production AI applications with CrewAI, **we recommend starting with a Flow**. + +While it's possible to run individual Crews or Agents, wrapping them in a Flow provides the necessary structure for a robust, scalable application. + +## Why Flows? + +1. **State Management**: Flows provide a built-in way to manage state across different steps of your application. This is crucial for passing data between Crews, maintaining context, and handling user inputs. +2. **Control**: Flows allow you to define precise execution paths, including loops, conditionals, and branching logic. This is essential for handling edge cases and ensuring your application behaves predictably. +3. **Observability**: Flows provide a clear structure that makes it easier to trace execution, debug issues, and monitor performance. We recommend using [CrewAI Tracing](/en/observability/tracing) for detailed insights. Simply run `crewai login` to enable free observability features. + +## The Architecture + +A typical production CrewAI application looks like this: + +```mermaid +graph TD + Start((Start)) --> Flow[Flow Orchestrator] + Flow --> State{State Management} + State --> Step1[Step 1: Data Gathering] + Step1 --> Crew1[Research Crew] + Crew1 --> State + State --> Step2{Condition Check} + Step2 -- "Valid" --> Step3[Step 3: Execution] + Step3 --> Crew2[Action Crew] + Step2 -- "Invalid" --> End((End)) + Crew2 --> End +``` + +### 1. The Flow Class +Your `Flow` class is the entry point. It defines the state schema and the methods that execute your logic. + +```python +from crewai.flow.flow import Flow, listen, start +from pydantic import BaseModel + +class AppState(BaseModel): + user_input: str = "" + research_results: str = "" + final_report: str = "" + +class ProductionFlow(Flow[AppState]): + @start() + def gather_input(self): + # ... logic to get input ... + pass + + @listen(gather_input) + def run_research_crew(self): + # ... trigger a Crew ... + pass +``` + +### 2. State Management +Use Pydantic models to define your state. This ensures type safety and makes it clear what data is available at each step. + +- **Keep it minimal**: Store only what you need to persist between steps. +- **Use structured data**: Avoid unstructured dictionaries when possible. + +### 3. Crews as Units of Work +Delegate complex tasks to Crews. A Crew should be focused on a specific goal (e.g., "Research a topic", "Write a blog post"). + +- **Don't over-engineer Crews**: Keep them focused. +- **Pass state explicitly**: Pass the necessary data from the Flow state to the Crew inputs. + +```python + @listen(gather_input) + def run_research_crew(self): + crew = ResearchCrew() + result = crew.kickoff(inputs={"topic": self.state.user_input}) + self.state.research_results = result.raw +``` + +## Control Primitives + +Leverage CrewAI's control primitives to add robustness and control to your Crews. + +### 1. Task Guardrails +Use [Task Guardrails](/en/concepts/tasks#task-guardrails) to validate task outputs before they are accepted. This ensures that your agents produce high-quality results. + +```python +def validate_content(result: TaskOutput) -> Tuple[bool, Any]: + if len(result.raw) < 100: + return (False, "Content is too short. Please expand.") + return (True, result.raw) + +task = Task( + ..., + guardrail=validate_content +) +``` + +### 2. Structured Outputs +Always use structured outputs (`output_pydantic` or `output_json`) when passing data between tasks or to your application. This prevents parsing errors and ensures type safety. + +```python +class ResearchResult(BaseModel): + summary: str + sources: List[str] + +task = Task( + ..., + output_pydantic=ResearchResult +) +``` + +### 3. LLM Hooks +Use [LLM Hooks](/en/learn/llm-hooks) to inspect or modify messages before they are sent to the LLM, or to sanitize responses. + +```python +@before_llm_call +def log_request(context): + print(f"Agent {context.agent.role} is calling the LLM...") +``` + +## Deployment Patterns + +When deploying your Flow, consider the following: + +### CrewAI Enterprise +The easiest way to deploy your Flow is using CrewAI Enterprise. It handles the infrastructure, authentication, and monitoring for you. + +Check out the [Deployment Guide](/en/enterprise/guides/deploy-crew) to get started. + +```bash +crewai deploy create +``` + +### Async Execution +For long-running tasks, use `kickoff_async` to avoid blocking your API. + +### Persistence +Use the `@persist` decorator to save the state of your Flow to a database. This allows you to resume execution if the process crashes or if you need to wait for human input. + +```python +@persist +class ProductionFlow(Flow[AppState]): + # ... +``` + +## Summary + +- **Start with a Flow.** +- **Define a clear State.** +- **Use Crews for complex tasks.** +- **Deploy with an API and persistence.** diff --git a/docs/en/introduction.mdx b/docs/en/introduction.mdx index 0079d4e4c..8804fb022 100644 --- a/docs/en/introduction.mdx +++ b/docs/en/introduction.mdx @@ -7,110 +7,89 @@ mode: "wide" # What is CrewAI? -**CrewAI is a lean, lightning-fast Python framework built entirely from scratch—completely independent of LangChain or other agent frameworks.** +**CrewAI is the leading open-source framework for orchestrating autonomous AI agents and building complex workflows.** -CrewAI empowers developers with both high-level simplicity and precise low-level control, ideal for creating autonomous AI agents tailored to any scenario: +It empowers developers to build production-ready multi-agent systems by combining the collaborative intelligence of **Crews** with the precise control of **Flows**. -- **[CrewAI Crews](/en/guides/crews/first-crew)**: Optimize for autonomy and collaborative intelligence, enabling you to create AI teams where each agent has specific roles, tools, and goals. -- **[CrewAI Flows](/en/guides/flows/first-flow)**: Enable granular, event-driven control, single LLM calls for precise task orchestration and supports Crews natively. +- **[CrewAI Flows](/en/guides/flows/first-flow)**: The backbone of your AI application. Flows allow you to create structured, event-driven workflows that manage state and control execution. They provide the scaffolding for your AI agents to work within. +- **[CrewAI Crews](/en/guides/crews/first-crew)**: The units of work within your Flow. Crews are teams of autonomous agents that collaborate to solve specific tasks delegated to them by the Flow. -With over 100,000 developers certified through our community courses, CrewAI is rapidly becoming the standard for enterprise-ready AI automation. +With over 100,000 developers certified through our community courses, CrewAI is the standard for enterprise-ready AI automation. +## The CrewAI Architecture -## How Crews Work +CrewAI's architecture is designed to balance autonomy with control. + +### 1. Flows: The Backbone - Just like a company has departments (Sales, Engineering, Marketing) working together under leadership to achieve business goals, CrewAI helps you create an organization of AI agents with specialized roles collaborating to accomplish complex tasks. - - - - CrewAI Framework Overview - - -| Component | Description | Key Features | -|:----------|:-----------:|:------------| -| **Crew** | The top-level organization | • Manages AI agent teams
• Oversees workflows
• Ensures collaboration
• Delivers outcomes | -| **AI Agents** | Specialized team members | • Have specific roles (researcher, writer)
• Use designated tools
• Can delegate tasks
• Make autonomous decisions | -| **Process** | Workflow management system | • Defines collaboration patterns
• Controls task assignments
• Manages interactions
• Ensures efficient execution | -| **Tasks** | Individual assignments | • Have clear objectives
• Use specific tools
• Feed into larger process
• Produce actionable results | - -### How It All Works Together - -1. The **Crew** organizes the overall operation -2. **AI Agents** work on their specialized tasks -3. The **Process** ensures smooth collaboration -4. **Tasks** get completed to achieve the goal - -## Key Features - - - - Create specialized agents with defined roles, expertise, and goals - from researchers to analysts to writers - - - Equip agents with custom tools and APIs to interact with external services and data sources - - - Agents work together, sharing insights and coordinating tasks to achieve complex objectives - - - Define sequential or parallel workflows, with agents automatically handling task dependencies - - - -## How Flows Work - - - While Crews excel at autonomous collaboration, Flows provide structured automations, offering granular control over workflow execution. Flows ensure tasks are executed reliably, securely, and efficiently, handling conditional logic, loops, and dynamic state management with precision. Flows integrate seamlessly with Crews, enabling you to balance high autonomy with exacting control. + Think of a Flow as the "manager" or the "process definition" of your application. It defines the steps, the logic, and how data moves through your system. CrewAI Framework Overview -| Component | Description | Key Features | -|:----------|:-----------:|:------------| -| **Flow** | Structured workflow orchestration | • Manages execution paths
• Handles state transitions
• Controls task sequencing
• Ensures reliable execution | -| **Events** | Triggers for workflow actions | • Initiate specific processes
• Enable dynamic responses
• Support conditional branching
• Allow for real-time adaptation | -| **States** | Workflow execution contexts | • Maintain execution data
• Enable persistence
• Support resumability
• Ensure execution integrity | -| **Crew Support** | Enhances workflow automation | • Injects pockets of agency when needed
• Complements structured workflows
• Balances automation with intelligence
• Enables adaptive decision-making | +Flows provide: +- **State Management**: Persist data across steps and executions. +- **Event-Driven Execution**: Trigger actions based on events or external inputs. +- **Control Flow**: Use conditional logic, loops, and branching. -### Key Capabilities +### 2. Crews: The Intelligence + + + Crews are the "teams" that do the heavy lifting. Within a Flow, you can trigger a Crew to tackle a complex problem requiring creativity and collaboration. + + + + CrewAI Framework Overview + + +Crews provide: +- **Role-Playing Agents**: Specialized agents with specific goals and tools. +- **Autonomous Collaboration**: Agents work together to solve tasks. +- **Task Delegation**: Tasks are assigned and executed based on agent capabilities. + +## How It All Works Together + +1. **The Flow** triggers an event or starts a process. +2. **The Flow** manages the state and decides what to do next. +3. **The Flow** delegates a complex task to a **Crew**. +4. **The Crew**'s agents collaborate to complete the task. +5. **The Crew** returns the result to the **Flow**. +6. **The Flow** continues execution based on the result. + +## Key Features - - Define precise execution paths responding dynamically to events + + Build reliable, stateful workflows that can handle long-running processes and complex logic. - - Manage workflow states and conditional execution securely and efficiently + + Deploy teams of agents that can plan, execute, and collaborate to achieve high-level goals. - - Effortlessly combine with Crews for enhanced autonomy and intelligence + + Connect your agents to any API, database, or local tool. - - Ensure predictable outcomes with explicit control flow and error handling + + Designed with security and compliance in mind for enterprise deployments. ## When to Use Crews vs. Flows - - Understanding when to use [Crews](/en/guides/crews/first-crew) versus [Flows](/en/guides/flows/first-flow) is key to maximizing the potential of CrewAI in your applications. - +**The short answer: Use both.** -| Use Case | Recommended Approach | Why? | -|:---------|:---------------------|:-----| -| **Open-ended research** | [Crews](/en/guides/crews/first-crew) | When tasks require creative thinking, exploration, and adaptation | -| **Content generation** | [Crews](/en/guides/crews/first-crew) | For collaborative creation of articles, reports, or marketing materials | -| **Decision workflows** | [Flows](/en/guides/flows/first-flow) | When you need predictable, auditable decision paths with precise control | -| **API orchestration** | [Flows](/en/guides/flows/first-flow) | For reliable integration with multiple external services in a specific sequence | -| **Hybrid applications** | Combined approach | Use [Flows](/en/guides/flows/first-flow) to orchestrate overall process with [Crews](/en/guides/crews/first-crew) handling complex subtasks | +For any production-ready application, **start with a Flow**. -### Decision Framework +- **Use a Flow** to define the overall structure, state, and logic of your application. +- **Use a Crew** within a Flow step when you need a team of agents to perform a specific, complex task that requires autonomy. -- **Choose [Crews](/en/guides/crews/first-crew) when:** You need autonomous problem-solving, creative collaboration, or exploratory tasks -- **Choose [Flows](/en/guides/flows/first-flow) when:** You require deterministic outcomes, auditability, or precise control over execution -- **Combine both when:** Your application needs both structured processes and pockets of autonomous intelligence +| Use Case | Architecture | +| :--- | :--- | +| **Simple Automation** | Single Flow with Python tasks | +| **Complex Research** | Flow managing state -> Crew performing research | +| **Application Backend** | Flow handling API requests -> Crew generating content -> Flow saving to DB | ## Why Choose CrewAI? @@ -124,13 +103,6 @@ With over 100,000 developers certified through our community courses, CrewAI is ## Ready to Start Building? - - Step-by-step tutorial to create a collaborative AI team that works together to solve complex problems. - Learn how to create structured, event-driven workflows with precise control over execution. + + Step-by-step tutorial to create a collaborative AI team that works together to solve complex problems. + diff --git a/docs/ko/concepts/production-architecture.mdx b/docs/ko/concepts/production-architecture.mdx new file mode 100644 index 000000000..fa33197dd --- /dev/null +++ b/docs/ko/concepts/production-architecture.mdx @@ -0,0 +1,154 @@ +--- +title: 프로덕션 아키텍처 +description: CrewAI로 프로덕션 수준의 AI 애플리케이션을 구축하기 위한 모범 사례 +icon: server +mode: "wide" +--- + +# Flow 우선 사고방식 (Flow-First Mindset) + +CrewAI로 프로덕션 AI 애플리케이션을 구축할 때는 **Flow로 시작하는 것을 권장합니다**. + +개별 Crews나 Agents를 실행하는 것도 가능하지만, 이를 Flow로 감싸면 견고하고 확장 가능한 애플리케이션에 필요한 구조를 제공합니다. + +## 왜 Flows인가? + +1. **상태 관리 (State Management)**: Flows는 애플리케이션의 여러 단계에 걸쳐 상태를 관리하는 내장된 방법을 제공합니다. 이는 Crews 간에 데이터를 전달하고, 컨텍스트를 유지하며, 사용자 입력을 처리하는 데 중요합니다. +2. **제어 (Control)**: Flows를 사용하면 루프, 조건문, 분기 로직을 포함한 정확한 실행 경로를 정의할 수 있습니다. 이는 예외 상황을 처리하고 애플리케이션이 예측 가능하게 동작하도록 보장하는 데 필수적입니다. +3. **관측 가능성 (Observability)**: Flows는 실행을 추적하고, 문제를 디버깅하며, 성능을 모니터링하기 쉽게 만드는 명확한 구조를 제공합니다. 자세한 통찰력을 얻으려면 [CrewAI Tracing](/ko/observability/tracing)을 사용하는 것이 좋습니다. `crewai login`을 실행하여 무료 관측 가능성 기능을 활성화하세요. + +## 아키텍처 + +일반적인 프로덕션 CrewAI 애플리케이션은 다음과 같습니다: + +```mermaid +graph TD + Start((시작)) --> Flow[Flow 오케스트레이터] + Flow --> State{상태 관리} + State --> Step1[1단계: 데이터 수집] + Step1 --> Crew1[연구 Crew] + Crew1 --> State + State --> Step2{조건 확인} + Step2 -- "유효함" --> Step3[3단계: 실행] + Step3 --> Crew2[액션 Crew] + Step2 -- "유효하지 않음" --> End((종료)) + Crew2 --> End +``` + +### 1. Flow 클래스 +`Flow` 클래스는 진입점입니다. 상태 스키마와 로직을 실행하는 메서드를 정의합니다. + +```python +from crewai.flow.flow import Flow, listen, start +from pydantic import BaseModel + +class AppState(BaseModel): + user_input: str = "" + research_results: str = "" + final_report: str = "" + +class ProductionFlow(Flow[AppState]): + @start() + def gather_input(self): + # ... 입력 받는 로직 ... + pass + + @listen(gather_input) + def run_research_crew(self): + # ... Crew 트리거 ... + pass +``` + +### 2. 상태 관리 (State Management) +Pydantic 모델을 사용하여 상태를 정의하세요. 이는 타입 안전성을 보장하고 각 단계에서 어떤 데이터를 사용할 수 있는지 명확하게 합니다. + +- **최소한으로 유지**: 단계 간에 유지해야 할 것만 저장하세요. +- **구조화된 데이터 사용**: 가능하면 비구조화된 딕셔너리는 피하세요. + +### 3. 작업 단위로서의 Crews +복잡한 작업은 Crews에게 위임하세요. Crew는 특정 목표(예: "주제 연구", "블로그 게시물 작성")에 집중해야 합니다. + +- **Crews를 과도하게 설계하지 마세요**: 집중력을 유지하세요. +- **상태를 명시적으로 전달하세요**: Flow 상태에서 필요한 데이터를 Crew 입력으로 전달하세요. + +```python + @listen(gather_input) + def run_research_crew(self): + crew = ResearchCrew() + result = crew.kickoff(inputs={"topic": self.state.user_input}) + self.state.research_results = result.raw +``` + +## Control Primitives + +CrewAI의 Control Primitives를 활용하여 Crew에 견고함과 제어력을 더하세요. + +### 1. Task Guardrails +[Task Guardrails](/ko/concepts/tasks#task-guardrails)를 사용하여 작업 결과가 수락되기 전에 유효성을 검사하세요. 이를 통해 agent가 고품질 결과를 생성하도록 보장할 수 있습니다. + +```python +def validate_content(result: TaskOutput) -> Tuple[bool, Any]: + if len(result.raw) < 100: + return (False, "Content is too short. Please expand.") + return (True, result.raw) + +task = Task( + ..., + guardrail=validate_content +) +``` + +### 2. 구조화된 출력 (Structured Outputs) +작업 간에 데이터를 전달하거나 애플리케이션으로 전달할 때는 항상 구조화된 출력(`output_pydantic` 또는 `output_json`)을 사용하세요. 이는 파싱 오류를 방지하고 타입 안전성을 보장합니다. + +```python +class ResearchResult(BaseModel): + summary: str + sources: List[str] + +task = Task( + ..., + output_pydantic=ResearchResult +) +``` + +### 3. LLM Hooks +[LLM Hooks](/ko/learn/llm-hooks)를 사용하여 LLM으로 전송되기 전에 메시지를 검사하거나 수정하고, 응답을 정리(sanitize)하세요. + +```python +@before_llm_call +def log_request(context): + print(f"Agent {context.agent.role} is calling the LLM...") +``` + +## 배포 패턴 + +Flow를 배포할 때 다음을 고려하세요: + +### CrewAI Enterprise +Flow를 배포하는 가장 쉬운 방법은 CrewAI Enterprise를 사용하는 것입니다. 인프라, 인증 및 모니터링을 대신 처리합니다. + +시작하려면 [배포 가이드](/ko/enterprise/guides/deploy-crew)를 확인하세요. + +```bash +crewai deploy create +``` + +### 비동기 실행 (Async Execution) +장기 실행 작업의 경우 `kickoff_async`를 사용하여 API 차단을 방지하세요. + +### 지속성 (Persistence) +`@persist` 데코레이터를 사용하여 Flow의 상태를 데이터베이스에 저장하세요. 이를 통해 프로세스가 중단되거나 사람의 입력을 기다려야 할 때 실행을 재개할 수 있습니다. + +```python +@persist +class ProductionFlow(Flow[AppState]): + # ... +``` + +## 요약 + +- **Flow로 시작하세요.** +- **명확한 State를 정의하세요.** +- **복잡한 작업에는 Crews를 사용하세요.** +- **API와 지속성을 갖추어 배포하세요.** diff --git a/docs/ko/introduction.mdx b/docs/ko/introduction.mdx index c78270d15..9d534a240 100644 --- a/docs/ko/introduction.mdx +++ b/docs/ko/introduction.mdx @@ -7,109 +7,89 @@ mode: "wide" # CrewAI란 무엇인가? -**CrewAI는 LangChain이나 기타 agent 프레임워크에 의존하지 않고, 완전히 독립적으로 처음부터 스크래치로 개발된 가볍고 매우 빠른 Python 프레임워크입니다.** +**CrewAI는 자율 AI agent를 조직하고 복잡한 workflow를 구축하기 위한 최고의 오픈 소스 프레임워크입니다.** -CrewAI는 고수준의 간편함과 정밀한 저수준 제어를 모두 제공하여, 어떤 시나리오에도 맞춤화된 자율 AI agent를 만드는 데 이상적입니다: +**Crews**의 협업 지능과 **Flows**의 정밀한 제어를 결합하여 개발자가 프로덕션 수준의 멀티 에이전트 시스템을 구축할 수 있도록 지원합니다. -- **[CrewAI Crews](/ko/guides/crews/first-crew)**: 자율성과 협업 지능을 극대화하여, 각 agent가 특정 역할, 도구, 목표를 가진 AI 팀을 만들 수 있습니다. -- **[CrewAI Flows](/ko/guides/flows/first-flow)**: 이벤트 기반의 세밀한 제어와 단일 LLM 호출을 통한 정확한 작업 orchestration을 지원하며, Crews와 네이티브로 통합됩니다. +- **[CrewAI Flows](/ko/guides/flows/first-flow)**: AI 애플리케이션의 중추(Backbone)입니다. Flows를 사용하면 상태를 관리하고 실행을 제어하는 구조화된 이벤트 기반 workflow를 만들 수 있습니다. AI agent가 작업할 수 있는 기반을 제공합니다. +- **[CrewAI Crews](/ko/guides/crews/first-crew)**: Flow 내의 작업 단위입니다. Crews는 Flow가 위임한 특정 작업을 해결하기 위해 협력하는 자율 agent 팀입니다. -10만 명이 넘는 개발자가 커뮤니티 과정을 통해 인증을 받았으며, CrewAI는 기업용 AI 자동화의 표준으로 빠르게 자리잡고 있습니다. +10만 명이 넘는 개발자가 커뮤니티 과정을 통해 인증을 받았으며, CrewAI는 기업용 AI 자동화의 표준입니다. -## Crew의 작동 방식 +## CrewAI 아키텍처 + +CrewAI의 아키텍처는 자율성과 제어의 균형을 맞추도록 설계되었습니다. + +### 1. Flows: 중추 (Backbone) - 회사가 비즈니스 목표를 달성하기 위해 여러 부서(영업, 엔지니어링, 마케팅 등)가 리더십 아래에서 함께 일하는 것처럼, CrewAI는 복잡한 작업을 달성하기 위해 전문화된 역할의 AI agent들이 협력하는 조직을 만들 수 있도록 도와줍니다. - - - - CrewAI Framework Overview - - -| 구성 요소 | 설명 | 주요 특징 | -|:----------|:----:|:----------| -| **Crew** | 최상위 조직 | • AI agent 팀 관리
• workflow 감독
• 협업 보장
• 결과 전달 | -| **AI agents** | 전문 팀원 | • 특정 역할 보유(Researcher, Writer 등)
• 지정된 도구 사용
• 작업 위임 가능
• 자율적 의사결정 가능 | -| **Process** | workflow 관리 시스템 | • 협업 패턴 정의
• 작업 할당 제어
• 상호작용 관리
• 효율적 실행 보장 | -| **Task** | 개별 할당 | • 명확한 목표 보유
• 특정 도구 사용
• 더 큰 프로세스에 기여
• 실행 가능한 결과 도출 | - -### 전체 구조의 동작 방식 - -1. **Crew**가 전체 운영을 조직합니다 -2. **AI agents**가 자신들의 전문 작업을 수행합니다 -3. **Process**가 원활한 협업을 보장합니다 -4. **Tasks**가 완료되어 목표를 달성합니다 - -## 주요 기능 - - - - Researcher, Analyst, Writer 등 다양한 역할과 전문성, 목표를 가진 agent를 생성할 수 있습니다 - - - agent에게 외부 서비스 및 데이터 소스와 상호작용할 수 있는 맞춤형 도구와 API를 제공합니다 - - - agent들이 함께 작업하며, 인사이트를 공유하고 작업을 조율하여 복잡한 목표를 달성합니다 - - - 순차적 또는 병렬 workflow를 정의할 수 있으며, agent가 작업 의존성을 자동으로 처리합니다 - - - -## Flow의 작동 원리 - - - Crew가 자율 협업에 탁월하다면, Flow는 구조화된 자동화를 제공하여 workflow 실행에 대한 세밀한 제어를 제공합니다. Flow는 조건부 로직, 반복문, 동적 상태 관리를 정확하게 처리하면서 작업이 신뢰성 있게, 안전하게, 효율적으로 실행되도록 보장합니다. Flow는 Crew와 원활하게 통합되어 높은 자율성과 엄격한 제어의 균형을 이룰 수 있게 해줍니다. + Flow를 애플리케이션의 "관리자" 또는 "프로세스 정의"라고 생각하세요. 단계, 로직, 그리고 시스템 내에서 데이터가 이동하는 방식을 정의합니다. CrewAI Framework Overview -| 구성 요소 | 설명 | 주요 기능 | -|:----------|:-----------:|:------------| -| **Flow** | 구조화된 workflow orchestration | • 실행 경로 관리
• 상태 전환 처리
• 작업 순서 제어
• 신뢰성 있는 실행 보장 | -| **Events** | workflow 액션 트리거 | • 특정 프로세스 시작
• 동적 응답 가능
• 조건부 분기 지원
• 실시간 적응 허용 | -| **States** | workflow 실행 컨텍스트 | • 실행 데이터 유지
• 데이터 영속성 지원
• 재개 가능성 보장
• 실행 무결성 확보 | -| **Crew Support** | workflow 자동화 강화 | • 필요할 때 agency 삽입
• 구조화된 workflow 보완
• 자동화와 인텔리전스의 균형
• 적응적 의사결정 지원 | +Flows의 기능: +- **상태 관리**: 단계 및 실행 전반에 걸쳐 데이터를 유지합니다. +- **이벤트 기반 실행**: 이벤트 또는 외부 입력을 기반으로 작업을 트리거합니다. +- **제어 흐름**: 조건부 로직, 반복문, 분기를 사용합니다. -### 주요 기능 +### 2. Crews: 지능 (Intelligence) + + + Crews는 힘든 일을 처리하는 "팀"입니다. Flow 내에서 창의성과 협업이 필요한 복잡한 문제를 해결하기 위해 Crew를 트리거할 수 있습니다. + + + + CrewAI Framework Overview + + +Crews의 기능: +- **역할 수행 Agent**: 특정 목표와 도구를 가진 전문 agent입니다. +- **자율 협업**: agent들이 협력하여 작업을 해결합니다. +- **작업 위임**: agent의 능력에 따라 작업이 할당되고 실행됩니다. + +## 전체 작동 방식 + +1. **Flow**가 이벤트를 트리거하거나 프로세스를 시작합니다. +2. **Flow**가 상태를 관리하고 다음에 무엇을 할지 결정합니다. +3. **Flow**가 복잡한 작업을 **Crew**에게 위임합니다. +4. **Crew**의 agent들이 협력하여 작업을 완료합니다. +5. **Crew**가 결과를 **Flow**에 반환합니다. +6. **Flow**가 결과를 바탕으로 실행을 계속합니다. + +## 주요 기능 - - 이벤트에 동적으로 반응하여 정밀한 실행 경로를 정의합니다 + + 장기 실행 프로세스와 복잡한 로직을 처리할 수 있는 신뢰할 수 있고 상태를 유지하는 workflow를 구축합니다. - - workflow 상태와 조건부 실행을 안전하고 효율적으로 관리합니다 + + 높은 수준의 목표를 달성하기 위해 계획하고, 실행하고, 협력할 수 있는 agent 팀을 배포합니다. - - Crews와 손쉽게 결합하여 자율성과 지능을 강화합니다 + + agent를 모든 API, 데이터베이스 또는 로컬 도구에 연결합니다. - - 명시적 제어 흐름과 오류 처리로 예측 가능한 결과를 보장합니다 + + 엔터프라이즈 배포를 위한 보안 및 규정 준수를 고려하여 설계되었습니다. -## Crew와 Flow를 언제 사용할까 +## Crews vs Flows 사용 시기 - - [Crew](/ko/guides/crews/first-crew)와 [Flow](/ko/guides/flows/first-flow)를 언제 사용할지 이해하는 것은 CrewAI의 잠재력을 애플리케이션에서 극대화하는 데 핵심적입니다. - +**짧은 답변: 둘 다 사용하세요.** -| 사용 사례 | 권장 접근 방식 | 이유 | -|:---------|:---------------------|:-----| -| **개방형 연구** | [Crew](/ko/guides/crews/first-crew) | 창의적 사고, 탐색, 적응이 필요한 작업에 적합 | -| **콘텐츠 생성** | [Crew](/ko/guides/crews/first-crew) | 기사, 보고서, 마케팅 자료 등 협업형 생성에 적합 | -| **의사결정 workflow** | [Flow](/ko/guides/flows/first-flow) | 예측 가능하고 감사 가능한 의사결정 경로 및 정밀 제어가 필요할 때 | -| **API orchestration** | [Flow](/ko/guides/flows/first-flow) | 특정 순서로 여러 외부 서비스에 신뢰성 있게 통합할 때 | -| **하이브리드 애플리케이션** | 혼합 접근 방식 | [Flow](/ko/guides/flows/first-flow)로 전체 프로세스를 orchestration하고, [Crew](/ko/guides/crews/first-crew)로 복잡한 하위 작업을 처리 | +모든 프로덕션 애플리케이션의 경우, **Flow로 시작하세요**. -### 의사결정 프레임워크 +- 애플리케이션의 전체 구조, 상태, 로직을 정의하려면 **Flow를 사용하세요**. +- 자율성이 필요한 특정하고 복잡한 작업을 수행하기 위해 agent 팀이 필요할 때 Flow 단계 내에서 **Crew를 사용하세요**. -- **[Crews](/ko/guides/crews/first-crew)를 선택할 때:** 자율적인 문제 해결, 창의적 협업 또는 탐구적 작업이 필요할 때 -- **[Flows](/ko/guides/flows/first-flow)를 선택할 때:** 결정론적 결과, 감사 가능성, 또는 실행에 대한 정밀한 제어가 필요할 때 -- **둘 다 결합할 때:** 애플리케이션에 구조화된 프로세스와 자율적 지능이 모두 필요할 때 +| 사용 사례 | 아키텍처 | +| :--- | :--- | +| **간단한 자동화** | Python 작업이 포함된 단일 Flow | +| **복잡한 연구** | 상태를 관리하는 Flow -> 연구를 수행하는 Crew | +| **애플리케이션 백엔드** | API 요청을 처리하는 Flow -> 콘텐츠를 생성하는 Crew -> DB에 저장하는 Flow | ## CrewAI를 선택해야 하는 이유? @@ -123,13 +103,6 @@ CrewAI는 고수준의 간편함과 정밀한 저수준 제어를 모두 제공 ## 지금 바로 빌드를 시작해보세요! - - 복잡한 문제를 함께 해결하는 협업 AI 팀을 단계별로 만드는 튜토리얼입니다. - 실행을 정밀하게 제어할 수 있는 구조화된, 이벤트 기반 workflow를 만드는 방법을 배워보세요. + + 복잡한 문제를 함께 해결하는 협업 AI 팀을 단계별로 만드는 튜토리얼입니다. + @@ -161,4 +141,4 @@ CrewAI는 고수준의 간편함과 정밀한 저수준 제어를 모두 제공 > 다른 개발자와 소통하며, 도움을 받고 CrewAI 경험을 공유해보세요. - \ No newline at end of file +
diff --git a/docs/ko/observability/tracing.mdx b/docs/ko/observability/tracing.mdx new file mode 100644 index 000000000..5551523b5 --- /dev/null +++ b/docs/ko/observability/tracing.mdx @@ -0,0 +1,213 @@ +--- +title: CrewAI Tracing +description: CrewAI AOP 플랫폼을 사용한 CrewAI Crews 및 Flows의 내장 추적 +icon: magnifying-glass-chart +mode: "wide" +--- + +# CrewAI 내장 추적 (Built-in Tracing) + +CrewAI는 Crews와 Flows를 실시간으로 모니터링하고 디버깅할 수 있는 내장 추적 기능을 제공합니다. 이 가이드는 CrewAI의 통합 관측 가능성 플랫폼을 사용하여 **Crews**와 **Flows** 모두에 대한 추적을 활성화하는 방법을 보여줍니다. + +> **CrewAI Tracing이란?** CrewAI의 내장 추적은 agent 결정, 작업 실행 타임라인, 도구 사용, LLM 호출을 포함한 AI agent에 대한 포괄적인 관측 가능성을 제공하며, 모두 [CrewAI AOP 플랫폼](https://app.crewai.com)을 통해 액세스할 수 있습니다. + +![CrewAI Tracing Interface](/images/crewai-tracing.png) + +## 사전 요구 사항 + +CrewAI 추적을 사용하기 전에 다음이 필요합니다: + +1. **CrewAI AOP 계정**: [app.crewai.com](https://app.crewai.com)에서 무료 계정에 가입하세요 +2. **CLI 인증**: CrewAI CLI를 사용하여 로컬 환경을 인증하세요 + +```bash +crewai login +``` + +## 설정 지침 + +### 1단계: CrewAI AOP 계정 생성 + +[app.crewai.com](https://app.crewai.com)을 방문하여 무료 계정을 만드세요. 이를 통해 추적, 메트릭을 보고 crews를 관리할 수 있는 CrewAI AOP 플랫폼에 액세스할 수 있습니다. + +### 2단계: CrewAI CLI 설치 및 인증 + +아직 설치하지 않았다면 CLI 도구와 함께 CrewAI를 설치하세요: + +```bash +uv add crewai[tools] +``` + +그런 다음 CrewAI AOP 계정으로 CLI를 인증하세요: + +```bash +crewai login +``` + +이 명령은 다음을 수행합니다: +1. 브라우저에서 인증 페이지를 엽니다 +2. 장치 코드를 입력하라는 메시지를 표시합니다 +3. CrewAI AOP 계정으로 로컬 환경을 인증합니다 +4. 로컬 개발을 위한 추적 기능을 활성화합니다 + +### 3단계: Crew에서 추적 활성화 + +`tracing` 매개변수를 `True`로 설정하여 Crew에 대한 추적을 활성화할 수 있습니다: + +```python +from crewai import Agent, Crew, Process, Task +from crewai_tools import SerperDevTool + +# Define your agents +researcher = Agent( + role="Senior Research Analyst", + goal="Uncover cutting-edge developments in AI and data science", + backstory=\"\"\"You work at a leading tech think tank. + Your expertise lies in identifying emerging trends. + You have a knack for dissecting complex data and presenting actionable insights.\"\"\", + verbose=True, + tools=[SerperDevTool()], +) + +writer = Agent( + role="Tech Content Strategist", + goal="Craft compelling content on tech advancements", + backstory=\"\"\"You are a renowned Content Strategist, known for your insightful and engaging articles. + You transform complex concepts into compelling narratives.\"\"\", + verbose=True, +) + +# Create tasks for your agents +research_task = Task( + description=\"\"\"Conduct a comprehensive analysis of the latest advancements in AI in 2024. + Identify key trends, breakthrough technologies, and potential industry impacts.\"\"\", + expected_output="Full analysis report in bullet points", + agent=researcher, +) + +writing_task = Task( + description=\"\"\"Using the insights provided, develop an engaging blog + post that highlights the most significant AI advancements. + Your post should be informative yet accessible, catering to a tech-savvy audience.\"\"\", + expected_output="Full blog post of at least 4 paragraphs", + agent=writer, +) + +# Enable tracing in your crew +crew = Crew( + agents=[researcher, writer], + tasks=[research_task, writing_task], + process=Process.sequential, + tracing=True, # Enable built-in tracing + verbose=True +) + +# Execute your crew +result = crew.kickoff() +``` + +### 4단계: Flow에서 추적 활성화 + +마찬가지로 CrewAI Flows에 대한 추적을 활성화할 수 있습니다: + +```python +from crewai.flow.flow import Flow, listen, start +from pydantic import BaseModel + +class ExampleState(BaseModel): + counter: int = 0 + message: str = "" + +class ExampleFlow(Flow[ExampleState]): + def __init__(self): + super().__init__(tracing=True) # Enable tracing for the flow + + @start() + def first_method(self): + print("Starting the flow") + self.state.counter = 1 + self.state.message = "Flow started" + return "continue" + + @listen("continue") + def second_method(self): + print("Continuing the flow") + self.state.counter += 1 + self.state.message = "Flow continued" + return "finish" + + @listen("finish") + def final_method(self): + print("Finishing the flow") + self.state.counter += 1 + self.state.message = "Flow completed" + +# Create and run the flow with tracing enabled +flow = ExampleFlow(tracing=True) +result = flow.kickoff() +``` + +### 5단계: CrewAI AOP 대시보드에서 추적 보기 + +crew 또는 flow를 실행한 후 CrewAI AOP 대시보드에서 CrewAI 애플리케이션이 생성한 추적을 볼 수 있습니다. agent 상호 작용, 도구 사용 및 LLM 호출의 세부 단계를 볼 수 있습니다. +아래 링크를 클릭하여 추적을 보거나 대시보드의 추적 탭으로 이동하세요 [여기](https://app.crewai.com/crewai_plus/trace_batches) +![CrewAI Tracing Interface](/images/view-traces.png) + + +### 대안: 환경 변수 구성 + +환경 변수를 설정하여 전역적으로 추적을 활성화할 수도 있습니다: + +```bash +export CREWAI_TRACING_ENABLED=true +``` + +또는 `.env` 파일에 추가하세요: + +```env +CREWAI_TRACING_ENABLED=true +``` + +이 환경 변수가 설정되면 `tracing=True`를 명시적으로 설정하지 않아도 모든 Crews와 Flows에 자동으로 추적이 활성화됩니다. + +## 추적 보기 + +### CrewAI AOP 대시보드 액세스 + +1. [app.crewai.com](https://app.crewai.com)을 방문하여 계정에 로그인하세요 +2. 프로젝트 대시보드로 이동하세요 +3. **Traces** 탭을 클릭하여 실행 세부 정보를 확인하세요 + +### 추적에서 볼 수 있는 내용 + +CrewAI 추적은 다음에 대한 포괄적인 가시성을 제공합니다: + +- **Agent 결정**: agent가 작업을 통해 어떻게 추론하고 결정을 내리는지 확인하세요 +- **작업 실행 타임라인**: 작업 시퀀스 및 종속성의 시각적 표현 +- **도구 사용**: 어떤 도구가 호출되고 그 결과를 모니터링하세요 +- **LLM 호출**: 프롬프트 및 응답을 포함한 모든 언어 모델 상호 작용을 추적하세요 +- **성능 메트릭**: 실행 시간, 토큰 사용량 및 비용 +- **오류 추적**: 세부 오류 정보 및 스택 추적 + +### 추적 기능 +- **실행 타임라인**: 실행의 다양한 단계를 클릭하여 확인하세요 +- **세부 로그**: 디버깅을 위한 포괄적인 로그에 액세스하세요 +- **성능 분석**: 실행 패턴을 분석하고 성능을 최적화하세요 +- **내보내기 기능**: 추가 분석을 위해 추적을 다운로드하세요 + +### 인증 문제 + +인증 문제가 발생하는 경우: + +1. 로그인되어 있는지 확인하세요: `crewai login` +2. 인터넷 연결을 확인하세요 +3. [app.crewai.com](https://app.crewai.com)에서 계정을 확인하세요 + +### 추적이 나타나지 않음 + +대시보드에 추적이 표시되지 않는 경우: + +1. Crew/Flow에서 `tracing=True`가 설정되어 있는지 확인하세요 +2. 환경 변수를 사용하는 경우 `CREWAI_TRACING_ENABLED=true`인지 확인하세요 +3. `crewai login`으로 인증되었는지 확인하세요 +4. crew/flow가 실제로 실행되고 있는지 확인하세요 diff --git a/docs/pt-BR/concepts/production-architecture.mdx b/docs/pt-BR/concepts/production-architecture.mdx new file mode 100644 index 000000000..f93d76a95 --- /dev/null +++ b/docs/pt-BR/concepts/production-architecture.mdx @@ -0,0 +1,154 @@ +--- +title: Arquitetura de Produção +description: Melhores práticas para construir aplicações de IA prontas para produção com CrewAI +icon: server +mode: "wide" +--- + +# A Mentalidade Flow-First + +Ao construir aplicações de IA de produção com CrewAI, **recomendamos começar com um Flow**. + +Embora seja possível executar Crews ou Agentes individuais, envolvê-los em um Flow fornece a estrutura necessária para uma aplicação robusta e escalável. + +## Por que Flows? + +1. **Gerenciamento de Estado**: Flows fornecem uma maneira integrada de gerenciar o estado em diferentes etapas da sua aplicação. Isso é crucial para passar dados entre Crews, manter o contexto e lidar com entradas do usuário. +2. **Controle**: Flows permitem definir caminhos de execução precisos, incluindo loops, condicionais e lógica de ramificação. Isso é essencial para lidar com casos extremos e garantir que sua aplicação se comporte de maneira previsível. +3. **Observabilidade**: Flows fornecem uma estrutura clara que facilita o rastreamento da execução, a depuração de problemas e o monitoramento do desempenho. Recomendamos o uso do [CrewAI Tracing](/pt-BR/observability/tracing) para insights detalhados. Basta executar `crewai login` para habilitar recursos de observabilidade gratuitos. + +## A Arquitetura + +Uma aplicação CrewAI de produção típica se parece com isso: + +```mermaid +graph TD + Start((Início)) --> Flow[Orquestrador de Flow] + Flow --> State{Gerenciamento de Estado} + State --> Step1[Etapa 1: Coleta de Dados] + Step1 --> Crew1[Crew de Pesquisa] + Crew1 --> State + State --> Step2{Verificação de Condição} + Step2 -- "Válido" --> Step3[Etapa 3: Execução] + Step3 --> Crew2[Crew de Ação] + Step2 -- "Inválido" --> End((Fim)) + Crew2 --> End +``` + +### 1. A Classe Flow +Sua classe `Flow` é o ponto de entrada. Ela define o esquema de estado e os métodos que executam sua lógica. + +```python +from crewai.flow.flow import Flow, listen, start +from pydantic import BaseModel + +class AppState(BaseModel): + user_input: str = "" + research_results: str = "" + final_report: str = "" + +class ProductionFlow(Flow[AppState]): + @start() + def gather_input(self): + # ... lógica para obter entrada ... + pass + + @listen(gather_input) + def run_research_crew(self): + # ... acionar um Crew ... + pass +``` + +### 2. Gerenciamento de Estado +Use modelos Pydantic para definir seu estado. Isso garante a segurança de tipos e deixa claro quais dados estão disponíveis em cada etapa. + +- **Mantenha o mínimo**: Armazene apenas o que você precisa persistir entre as etapas. +- **Use dados estruturados**: Evite dicionários não estruturados quando possível. + +### 3. Crews como Unidades de Trabalho +Delegue tarefas complexas para Crews. Um Crew deve ser focado em um objetivo específico (por exemplo, "Pesquisar um tópico", "Escrever uma postagem no blog"). + +- **Não superengendre Crews**: Mantenha-os focados. +- **Passe o estado explicitamente**: Passe os dados necessários do estado do Flow para as entradas do Crew. + +```python + @listen(gather_input) + def run_research_crew(self): + crew = ResearchCrew() + result = crew.kickoff(inputs={"topic": self.state.user_input}) + self.state.research_results = result.raw +``` + +## Primitivas de Controle + +Aproveite as primitivas de controle do CrewAI para adicionar robustez e controle aos seus Crews. + +### 1. Task Guardrails +Use [Task Guardrails](/pt-BR/concepts/tasks#task-guardrails) para validar as saídas das tarefas antes que sejam aceitas. Isso garante que seus agentes produzam resultados de alta qualidade. + +```python +def validate_content(result: TaskOutput) -> Tuple[bool, Any]: + if len(result.raw) < 100: + return (False, "Content is too short. Please expand.") + return (True, result.raw) + +task = Task( + ..., + guardrail=validate_content +) +``` + +### 2. Saídas Estruturadas +Sempre use saídas estruturadas (`output_pydantic` ou `output_json`) ao passar dados entre tarefas ou para sua aplicação. Isso evita erros de análise e garante a segurança de tipos. + +```python +class ResearchResult(BaseModel): + summary: str + sources: List[str] + +task = Task( + ..., + output_pydantic=ResearchResult +) +``` + +### 3. LLM Hooks +Use [LLM Hooks](/pt-BR/learn/llm-hooks) para inspecionar ou modificar mensagens antes que elas sejam enviadas para o LLM, ou para higienizar respostas. + +```python +@before_llm_call +def log_request(context): + print(f"Agent {context.agent.role} is calling the LLM...") +``` + +## Padrões de Implantação + +Ao implantar seu Flow, considere o seguinte: + +### CrewAI Enterprise +A maneira mais fácil de implantar seu Flow é usando o CrewAI Enterprise. Ele lida com a infraestrutura, autenticação e monitoramento para você. + +Confira o [Guia de Implantação](/pt-BR/enterprise/guides/deploy-crew) para começar. + +```bash +crewai deploy create +``` + +### Execução Assíncrona +Para tarefas de longa duração, use `kickoff_async` para evitar bloquear sua API. + +### Persistência +Use o decorador `@persist` para salvar o estado do seu Flow em um banco de dados. Isso permite retomar a execução se o processo falhar ou se você precisar esperar pela entrada humana. + +```python +@persist +class ProductionFlow(Flow[AppState]): + # ... +``` + +## Resumo + +- **Comece com um Flow.** +- **Defina um Estado claro.** +- **Use Crews para tarefas complexas.** +- **Implante com uma API e persistência.** diff --git a/docs/pt-BR/introduction.mdx b/docs/pt-BR/introduction.mdx index cc73bc039..6e0e922ff 100644 --- a/docs/pt-BR/introduction.mdx +++ b/docs/pt-BR/introduction.mdx @@ -7,110 +7,89 @@ mode: "wide" # O que é CrewAI? -**CrewAI é um framework Python enxuto e ultrarrápido, construído totalmente do zero—completamente independente do LangChain ou de outros frameworks de agentes.** +**CrewAI é o principal framework open-source para orquestrar agentes de IA autônomos e construir fluxos de trabalho complexos.** -O CrewAI capacita desenvolvedores tanto com simplicidade de alto nível quanto com controle detalhado de baixo nível, ideal para criar agentes de IA autônomos sob medida para qualquer cenário: +Ele capacita desenvolvedores a construir sistemas multi-agente prontos para produção, combinando a inteligência colaborativa dos **Crews** com o controle preciso dos **Flows**. -- **[Crews do CrewAI](/pt-BR/guides/crews/first-crew)**: Otimizados para autonomia e inteligência colaborativa, permitindo criar equipes de IA onde cada agente possui funções, ferramentas e objetivos específicos. -- **[Flows do CrewAI](/pt-BR/guides/flows/first-flow)**: Proporcionam controle granular, orientado por eventos, com chamadas LLM individuais para uma orquestração precisa das tarefas, além de suportar Crews nativamente. +- **[Flows do CrewAI](/pt-BR/guides/flows/first-flow)**: A espinha dorsal da sua aplicação de IA. Flows permitem criar fluxos de trabalho estruturados e orientados a eventos que gerenciam estado e controlam a execução. Eles fornecem a estrutura para seus agentes de IA trabalharem. +- **[Crews do CrewAI](/pt-BR/guides/crews/first-crew)**: As unidades de trabalho dentro do seu Flow. Crews são equipes de agentes autônomos que colaboram para resolver tarefas específicas delegadas a eles pelo Flow. -Com mais de 100.000 desenvolvedores certificados em nossos cursos comunitários, o CrewAI está se tornando rapidamente o padrão para automação de IA pronta para empresas. +Com mais de 100.000 desenvolvedores certificados em nossos cursos comunitários, o CrewAI é o padrão para automação de IA pronta para empresas. +## A Arquitetura do CrewAI -## Como funcionam os Crews +A arquitetura do CrewAI foi projetada para equilibrar autonomia com controle. + +### 1. Flows: A Espinha Dorsal - Assim como uma empresa possui departamentos (Vendas, Engenharia, Marketing) trabalhando juntos sob uma liderança para atingir objetivos de negócio, o CrewAI ajuda você a criar uma “organização” de agentes de IA com funções especializadas colaborando para realizar tarefas complexas. - - - - Visão Geral do Framework CrewAI - - -| Componente | Descrição | Principais Funcionalidades | -|:-----------|:-----------:|:-------------------------| -| **Crew** | Organização de mais alto nível | • Gerencia equipes de agentes de IA
• Supervisiona fluxos de trabalho
• Garante colaboração
• Entrega resultados | -| **Agentes de IA** | Membros especializados da equipe | • Possuem funções específicas (pesquisador, escritor)
• Utilizam ferramentas designadas
• Podem delegar tarefas
• Tomam decisões autônomas | -| **Process** | Sistema de gestão do fluxo de trabalho | • Define padrões de colaboração
• Controla designação de tarefas
• Gerencia interações
• Garante execução eficiente | -| **Tasks** | Atribuições individuais | • Objetivos claros
• Utilizam ferramentas específicas
• Alimentam processos maiores
• Geram resultados acionáveis | - -### Como tudo trabalha junto - -1. O **Crew** organiza toda a operação -2. **Agentes de IA** realizam tarefas especializadas -3. O **Process** garante colaboração fluida -4. **Tasks** são concluídas para alcançar o objetivo - -## Principais Funcionalidades - - - - Crie agentes especializados com funções, conhecimentos e objetivos definidos – de pesquisadores e analistas a escritores - - - Equipe os agentes com ferramentas e APIs personalizadas para interagir com serviços e fontes de dados externas - - - Agentes trabalham juntos, compartilhando insights e coordenando tarefas para conquistar objetivos complexos - - - Defina fluxos de trabalho sequenciais ou paralelos, com agentes lidando automaticamente com dependências entre tarefas - - - -## Como funcionam os Flows - - - Enquanto Crews se destacam na colaboração autônoma, Flows proporcionam automações estruturadas, oferecendo controle granular sobre a execução dos fluxos de trabalho. Flows garantem execução confiável, segura e eficiente, lidando com lógica condicional, loops e gerenciamento dinâmico de estados com precisão. Flows se integram perfeitamente com Crews, permitindo equilibrar alta autonomia com controle rigoroso. + Pense em um Flow como o "gerente" ou a "definição do processo" da sua aplicação. Ele define as etapas, a lógica e como os dados se movem através do seu sistema. Visão Geral do Framework CrewAI -| Componente | Descrição | Principais Funcionalidades | -|:-----------|:-----------:|:-------------------------| -| **Flow** | Orquestração de fluxo de trabalho estruturada | • Gerencia caminhos de execução
• Lida com transições de estado
• Controla a sequência de tarefas
• Garante execução confiável | -| **Events** | Gatilhos para ações nos fluxos | • Iniciam processos específicos
• Permitem respostas dinâmicas
• Suportam ramificações condicionais
• Adaptam-se em tempo real | -| **States** | Contextos de execução dos fluxos | • Mantêm dados de execução
• Permitem persistência
• Suportam retomada
• Garantem integridade na execução | -| **Crew Support** | Aprimora automação de fluxos | • Injeta autonomia quando necessário
• Complementa fluxos estruturados
• Equilibra automação e inteligência
• Permite tomada de decisão adaptativa | +Flows fornecem: +- **Gerenciamento de Estado**: Persistem dados através de etapas e execuções. +- **Execução Orientada a Eventos**: Acionam ações com base em eventos ou entradas externas. +- **Controle de Fluxo**: Usam lógica condicional, loops e ramificações. -### Capacidades-Chave +### 2. Crews: A Inteligência + + + Crews são as "equipes" que fazem o trabalho pesado. Dentro de um Flow, você pode acionar um Crew para lidar com um problema complexo que requer criatividade e colaboração. + + + + Visão Geral do Framework CrewAI + + +Crews fornecem: +- **Agentes com Funções**: Agentes especializados com objetivos e ferramentas específicas. +- **Colaboração Autônoma**: Agentes trabalham juntos para resolver tarefas. +- **Delegação de Tarefas**: Tarefas são atribuídas e executadas com base nas capacidades dos agentes. + +## Como Tudo Funciona Junto + +1. **O Flow** aciona um evento ou inicia um processo. +2. **O Flow** gerencia o estado e decide o que fazer a seguir. +3. **O Flow** delega uma tarefa complexa para um **Crew**. +4. Os agentes do **Crew** colaboram para completar a tarefa. +5. **O Crew** retorna o resultado para o **Flow**. +6. **O Flow** continua a execução com base no resultado. + +## Principais Funcionalidades - - Defina caminhos de execução precisos respondendo dinamicamente a eventos + + Construa fluxos de trabalho confiáveis e com estado que podem lidar com processos de longa duração e lógica complexa. - - Gerencie estados de fluxo de trabalho e execução condicional de forma segura e eficiente + + Implante equipes de agentes que podem planejar, executar e colaborar para alcançar objetivos de alto nível. - - Combine de forma simples com Crews para maior autonomia e inteligência + + Conecte seus agentes a qualquer API, banco de dados ou ferramenta local. - - Garanta resultados previsíveis com controle explícito de fluxo e tratamento de erros + + Projetado com segurança e conformidade em mente para implantações empresariais. -## Quando usar Crews versus Flows +## Quando usar Crews vs. Flows - - Entender quando utilizar [Crews](/pt-BR/guides/crews/first-crew) ou [Flows](/pt-BR/guides/flows/first-flow) é fundamental para maximizar o potencial do CrewAI em suas aplicações. - +**A resposta curta: Use ambos.** -| Caso de uso | Abordagem recomendada | Por quê? | -|:------------|:---------------------|:---------| -| **Pesquisa aberta** | [Crews](/pt-BR/guides/crews/first-crew) | Quando as tarefas exigem criatividade, exploração e adaptação | -| **Geração de conteúdo** | [Crews](/pt-BR/guides/crews/first-crew) | Para criação colaborativa de artigos, relatórios ou materiais de marketing | -| **Fluxos de decisão** | [Flows](/pt-BR/guides/flows/first-flow) | Quando é necessário caminhos de decisão previsíveis, auditáveis e com controle preciso | -| **Orquestração de APIs** | [Flows](/pt-BR/guides/flows/first-flow) | Para integração confiável com múltiplos serviços externos em sequência específica | -| **Aplicações híbridas** | Abordagem combinada | Use [Flows](/pt-BR/guides/flows/first-flow) para orquestrar o processo geral com [Crews](/pt-BR/guides/crews/first-crew) lidando com subtarefas complexas | +Para qualquer aplicação pronta para produção, **comece com um Flow**. -### Framework de Decisão +- **Use um Flow** para definir a estrutura geral, estado e lógica da sua aplicação. +- **Use um Crew** dentro de uma etapa do Flow quando precisar de uma equipe de agentes para realizar uma tarefa específica e complexa que requer autonomia. -- **Escolha [Crews](/pt-BR/guides/crews/first-crew) quando:** Precisa de resolução autônoma de problemas, colaboração criativa ou tarefas exploratórias -- **Escolha [Flows](/pt-BR/guides/flows/first-flow) quando:** Requer resultados determinísticos, auditabilidade ou controle preciso sobre a execução -- **Combine ambos quando:** Sua aplicação precisa de processos estruturados e também de bolsões de inteligência autônoma +| Caso de Uso | Arquitetura | +| :--- | :--- | +| **Automação Simples** | Flow único com tarefas Python | +| **Pesquisa Complexa** | Flow gerenciando estado -> Crew realizando pesquisa | +| **Backend de Aplicação** | Flow lidando com requisições API -> Crew gerando conteúdo -> Flow salvando no BD | ## Por que escolher o CrewAI? @@ -124,13 +103,6 @@ Com mais de 100.000 desenvolvedores certificados em nossos cursos comunitários, ## Pronto para começar a construir? - - Tutorial passo a passo para criar uma equipe de IA colaborativa que trabalha junto para resolver problemas complexos. - Aprenda a criar fluxos de trabalho estruturados e orientados por eventos com controle preciso de execução. + + Tutorial passo a passo para criar uma equipe de IA colaborativa que trabalha junto para resolver problemas complexos. + diff --git a/docs/pt-BR/observability/tracing.mdx b/docs/pt-BR/observability/tracing.mdx new file mode 100644 index 000000000..220f7ada1 --- /dev/null +++ b/docs/pt-BR/observability/tracing.mdx @@ -0,0 +1,213 @@ +--- +title: CrewAI Tracing +description: Rastreamento integrado para Crews e Flows do CrewAI com a plataforma CrewAI AOP +icon: magnifying-glass-chart +mode: "wide" +--- + +# Rastreamento Integrado do CrewAI + +O CrewAI fornece recursos de rastreamento integrados que permitem monitorar e depurar seus Crews e Flows em tempo real. Este guia demonstra como habilitar o rastreamento para **Crews** e **Flows** usando a plataforma de observabilidade integrada do CrewAI. + +> **O que é o CrewAI Tracing?** O rastreamento integrado do CrewAI fornece observabilidade abrangente para seus agentes de IA, incluindo decisões de agentes, cronogramas de execução de tarefas, uso de ferramentas e chamadas de LLM - tudo acessível através da [plataforma CrewAI AOP](https://app.crewai.com). + +![CrewAI Tracing Interface](/images/crewai-tracing.png) + +## Pré-requisitos + +Antes de usar o rastreamento do CrewAI, você precisa: + +1. **Conta CrewAI AOP**: Cadastre-se para uma conta gratuita em [app.crewai.com](https://app.crewai.com) +2. **Autenticação CLI**: Use a CLI do CrewAI para autenticar seu ambiente local + +```bash +crewai login +``` + +## Instruções de Configuração + +### Passo 1: Crie sua Conta CrewAI AOP + +Visite [app.crewai.com](https://app.crewai.com) e crie sua conta gratuita. Isso lhe dará acesso à plataforma CrewAI AOP, onde você pode visualizar rastreamentos, métricas e gerenciar seus crews. + +### Passo 2: Instale a CLI do CrewAI e Autentique + +Se você ainda não o fez, instale o CrewAI com as ferramentas CLI: + +```bash +uv add crewai[tools] +``` + +Em seguida, autentique sua CLI com sua conta CrewAI AOP: + +```bash +crewai login +``` + +Este comando irá: +1. Abrir seu navegador na página de autenticação +2. Solicitar que você insira um código de dispositivo +3. Autenticar seu ambiente local com sua conta CrewAI AOP +4. Habilitar recursos de rastreamento para seu desenvolvimento local + +### Passo 3: Habilite o Rastreamento em seu Crew + +Você pode habilitar o rastreamento para seu Crew definindo o parâmetro `tracing` como `True`: + +```python +from crewai import Agent, Crew, Process, Task +from crewai_tools import SerperDevTool + +# Define your agents +researcher = Agent( + role="Senior Research Analyst", + goal="Uncover cutting-edge developments in AI and data science", + backstory=\"\"\"You work at a leading tech think tank. + Your expertise lies in identifying emerging trends. + You have a knack for dissecting complex data and presenting actionable insights.\"\"\", + verbose=True, + tools=[SerperDevTool()], +) + +writer = Agent( + role="Tech Content Strategist", + goal="Craft compelling content on tech advancements", + backstory=\"\"\"You are a renowned Content Strategist, known for your insightful and engaging articles. + You transform complex concepts into compelling narratives.\"\"\", + verbose=True, +) + +# Create tasks for your agents +research_task = Task( + description=\"\"\"Conduct a comprehensive analysis of the latest advancements in AI in 2024. + Identify key trends, breakthrough technologies, and potential industry impacts.\"\"\", + expected_output="Full analysis report in bullet points", + agent=researcher, +) + +writing_task = Task( + description=\"\"\"Using the insights provided, develop an engaging blog + post that highlights the most significant AI advancements. + Your post should be informative yet accessible, catering to a tech-savvy audience.\"\"\", + expected_output="Full blog post of at least 4 paragraphs", + agent=writer, +) + +# Enable tracing in your crew +crew = Crew( + agents=[researcher, writer], + tasks=[research_task, writing_task], + process=Process.sequential, + tracing=True, # Enable built-in tracing + verbose=True +) + +# Execute your crew +result = crew.kickoff() +``` + +### Passo 4: Habilite o Rastreamento em seu Flow + +Da mesma forma, você pode habilitar o rastreamento para Flows do CrewAI: + +```python +from crewai.flow.flow import Flow, listen, start +from pydantic import BaseModel + +class ExampleState(BaseModel): + counter: int = 0 + message: str = "" + +class ExampleFlow(Flow[ExampleState]): + def __init__(self): + super().__init__(tracing=True) # Enable tracing for the flow + + @start() + def first_method(self): + print("Starting the flow") + self.state.counter = 1 + self.state.message = "Flow started" + return "continue" + + @listen("continue") + def second_method(self): + print("Continuing the flow") + self.state.counter += 1 + self.state.message = "Flow continued" + return "finish" + + @listen("finish") + def final_method(self): + print("Finishing the flow") + self.state.counter += 1 + self.state.message = "Flow completed" + +# Create and run the flow with tracing enabled +flow = ExampleFlow(tracing=True) +result = flow.kickoff() +``` + +### Passo 5: Visualize os Rastreamentos no Painel CrewAI AOP + +Após executar o crew ou flow, você pode visualizar os rastreamentos gerados pela sua aplicação CrewAI no painel CrewAI AOP. Você verá etapas detalhadas das interações dos agentes, usos de ferramentas e chamadas de LLM. +Basta clicar no link abaixo para visualizar os rastreamentos ou ir para a aba de rastreamentos no painel [aqui](https://app.crewai.com/crewai_plus/trace_batches) +![CrewAI Tracing Interface](/images/view-traces.png) + + +### Alternativa: Configuração de Variável de Ambiente + +Você também pode habilitar o rastreamento globalmente definindo uma variável de ambiente: + +```bash +export CREWAI_TRACING_ENABLED=true +``` + +Ou adicione-a ao seu arquivo `.env`: + +```env +CREWAI_TRACING_ENABLED=true +``` + +Quando esta variável de ambiente estiver definida, todos os Crews e Flows terão automaticamente o rastreamento habilitado, mesmo sem definir explicitamente `tracing=True`. + +## Visualizando seus Rastreamentos + +### Acesse o Painel CrewAI AOP + +1. Visite [app.crewai.com](https://app.crewai.com) e faça login em sua conta +2. Navegue até o painel do seu projeto +3. Clique na aba **Traces** para visualizar os detalhes de execução + +### O que Você Verá nos Rastreamentos + +O rastreamento do CrewAI fornece visibilidade abrangente sobre: + +- **Decisões dos Agentes**: Veja como os agentes raciocinam através das tarefas e tomam decisões +- **Cronograma de Execução de Tarefas**: Representação visual de sequências e dependências de tarefas +- **Uso de Ferramentas**: Monitore quais ferramentas são chamadas e seus resultados +- **Chamadas de LLM**: Rastreie todas as interações do modelo de linguagem, incluindo prompts e respostas +- **Métricas de Desempenho**: Tempos de execução, uso de tokens e custos +- **Rastreamento de Erros**: Informações detalhadas de erros e rastreamentos de pilha + +### Recursos de Rastreamento +- **Cronograma de Execução**: Clique através de diferentes estágios de execução +- **Logs Detalhados**: Acesse logs abrangentes para depuração +- **Análise de Desempenho**: Analise padrões de execução e otimize o desempenho +- **Capacidades de Exportação**: Baixe rastreamentos para análise adicional + +### Problemas de Autenticação + +Se você encontrar problemas de autenticação: + +1. Certifique-se de estar logado: `crewai login` +2. Verifique sua conexão com a internet +3. Verifique sua conta em [app.crewai.com](https://app.crewai.com) + +### Rastreamentos Não Aparecem + +Se os rastreamentos não estiverem aparecendo no painel: + +1. Confirme que `tracing=True` está definido em seu Crew/Flow +2. Verifique se `CREWAI_TRACING_ENABLED=true` se estiver usando variáveis de ambiente +3. Certifique-se de estar autenticado com `crewai login` +4. Verifique se seu crew/flow está realmente executando