diff --git a/README.md b/README.md index f396e48..a0ad640 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Overmind SDK [![CI Checks](https://github.com/overmind-core/overmind-python/actions/workflows/publish.yml/badge.svg)](https://github.com/overmind-core/overmind-python/actions/workflows/publish.yml) -[![PyPI version](https://img.shields.io/pypi/v/overmind-sdk.svg)](https://pypi.org/project/overmind-sdk/) +[![PyPI version](https://img.shields.io/pypi/v/overmind.svg)](https://pypi.org/project/overmind/) Automatic observability for LLM applications. One call to `init()` instruments your existing OpenAI, Anthropic, Google Gemini, or Agno code — no proxy, no key sharing, no import changes. @@ -16,16 +16,16 @@ Automatic observability for LLM applications. One call to `init()` instruments y ## Installation ```bash -pip install overmind-sdk +pip install overmind ``` Install alongside your LLM provider package: ```bash -pip install overmind-sdk openai # OpenAI -pip install overmind-sdk anthropic # Anthropic -pip install overmind-sdk google-genai # Google Gemini -pip install overmind-sdk agno # Agno +pip install overmind openai # OpenAI +pip install overmind anthropic # Anthropic +pip install overmind google-genai # Google Gemini +pip install overmind agno # Agno ``` --- @@ -41,7 +41,7 @@ Sign up at [console.overmindlab.ai](https://console.overmindlab.ai) — your API Call `init()` once at application startup, before any LLM calls: ```python -from overmind_sdk import init +from overmind import init init( overmind_api_key="ovr_...", # or set OVERMIND_API_KEY env var @@ -75,7 +75,7 @@ Traces appear in your [Overmind dashboard](https://console.overmindlab.ai) in re ### OpenAI ```python -from overmind_sdk import init +from overmind import init from openai import OpenAI init(service_name="my-service", providers=["openai"]) @@ -90,7 +90,7 @@ response = client.chat.completions.create( ### Anthropic ```python -from overmind_sdk import init +from overmind import init import anthropic init(service_name="my-service", providers=["anthropic"]) @@ -106,7 +106,7 @@ message = client.messages.create( ### Google Gemini ```python -from overmind_sdk import init +from overmind import init from google import genai init(service_name="my-service", providers=["google"]) @@ -121,7 +121,7 @@ response = client.models.generate_content( ### Agno ```python -from overmind_sdk import init +from overmind import init from agno.agent import Agent from agno.models.openai import OpenAIChat @@ -136,7 +136,7 @@ agent.print_response("Write a short poem about the sea.") Pass an empty `providers` list (or omit it) to automatically instrument every supported provider that is installed: ```python -from overmind_sdk import init +from overmind import init init(service_name="my-service") # auto-detects openai, anthropic, google, agno ``` @@ -173,7 +173,7 @@ init(service_name="my-service") # auto-detects openai, anthropic, google, agno Get the OpenTelemetry tracer to create custom spans around any block of code: ```python -from overmind_sdk import init, get_tracer +from overmind import init, get_tracer init(service_name="my-service") @@ -189,7 +189,7 @@ with tracer.start_as_current_span("process-document") as span: Tag the current trace with user identity. Call this in your request handler or middleware: ```python -from overmind_sdk import set_user +from overmind import set_user # In a FastAPI middleware: @app.middleware("http") @@ -213,7 +213,7 @@ async def add_user_context(request: Request, call_next): Add a custom attribute to the current span: ```python -from overmind_sdk import set_tag +from overmind import set_tag set_tag("feature.flag", "new-checkout-flow") set_tag("tenant.id", tenant_id) @@ -224,7 +224,7 @@ set_tag("tenant.id", tenant_id) Record an exception on the current span and mark it as an error: ```python -from overmind_sdk import capture_exception +from overmind import capture_exception try: result = risky_llm_call() @@ -239,7 +239,7 @@ except Exception as e: ```python import os -from overmind_sdk import init, get_tracer, set_user, set_tag, capture_exception +from overmind import init, get_tracer, set_user, set_tag, capture_exception from openai import OpenAI os.environ["OVERMIND_API_KEY"] = "ovr_your_key_here" diff --git a/overmind_sdk/__init__.py b/overmind/__init__.py similarity index 55% rename from overmind_sdk/__init__.py rename to overmind/__init__.py index b4afd41..e360338 100644 --- a/overmind_sdk/__init__.py +++ b/overmind/__init__.py @@ -1,35 +1,44 @@ """ Overmind Python Client -A Python client for the Overmind API that provides easy access to AI provider endpoints -with policy enforcement and automatic observability. +A Python client for the Overmind API that provides automatic observability for +LLM applications. """ -from .client import OvermindClient -from .exceptions import OvermindAPIError, OvermindAuthenticationError, OvermindError - -from .tracing import init, get_tracer, set_user, set_tag, capture_exception from opentelemetry.overmind.prompt import PromptString -from .tracer import observe, SpanType, function, entry_point, workflow, tool - +from .exceptions import OvermindAPIError, OvermindAuthenticationError, OvermindError +from .tracing import ( + SpanType, + capture_exception, + entry_point, + function, + get_tracer, + init, + observe, + set_tag, + set_user, + start_span, + tool, + workflow, +) -__version__ = "0.1.32" +__version__ = "0.1.39" __all__ = [ - "OvermindClient", - "OvermindError", "OvermindAPIError", "OvermindAuthenticationError", - "init", - "get_tracer", - "set_user", - "set_tag", - "capture_exception", + "OvermindError", "PromptString", - "observe", "SpanType", - "function", + "capture_exception", "entry_point", - "workflow", + "function", + "get_tracer", + "init", + "observe", + "set_tag", + "set_user", + "start_span", "tool", + "workflow", ] diff --git a/overmind/client.py b/overmind/client.py new file mode 100644 index 0000000..bc9f9b1 --- /dev/null +++ b/overmind/client.py @@ -0,0 +1,62 @@ +""" +Overmind layers client. +""" + +from collections.abc import Sequence +from functools import lru_cache + +import requests + +from .exceptions import OvermindAPIError +from .models import LayerResponse +from .utils.api_settings import get_api_settings + + +class OvermindLayersClient: + def __init__( + self, + overmind_api_key: str | None = None, + base_url: str | None = None, + traces_base_url: str | None = None, + ): + self.overmind_api_key, self.base_url = get_api_settings(overmind_api_key, base_url) + self.session = requests.Session() + self.session.headers.update( + { + "X-API-Token": self.overmind_api_key, + "Content-Type": "application/json", + } + ) + + def run_layer( + self, input_data: str, policies: Sequence[str | dict], layer_position: str, **kwargs + ) -> LayerResponse: + """ + Run a layer of the Overmind API. + """ + payload = { + "input_data": input_data, + "policies": policies, + "layer_position": layer_position, + "kwargs": kwargs, + } + + response_data = self.session.request("POST", f"{self.base_url}/api/v1/layers/run", json=payload) + + if response_data.status_code != 200: + raise OvermindAPIError( + message=response_data.text, + status_code=response_data.status_code, + response_data=response_data.json(), + ) + + return LayerResponse(**response_data.json()) + + +@lru_cache +def get_layers_client( + overmind_api_key: str | None = None, + base_url: str | None = None, + traces_base_url: str | None = None, +): + return OvermindLayersClient(overmind_api_key, base_url, traces_base_url) diff --git a/overmind_sdk/exceptions.py b/overmind/exceptions.py similarity index 84% rename from overmind_sdk/exceptions.py rename to overmind/exceptions.py index 99a01fa..cec72f1 100644 --- a/overmind_sdk/exceptions.py +++ b/overmind/exceptions.py @@ -6,20 +6,18 @@ class OvermindError(Exception): """Base exception for all Overmind client errors.""" - pass class OvermindAuthenticationError(OvermindError): """Raised when authentication fails.""" - pass class OvermindAPIError(OvermindError): """Raised when the API returns an error.""" def __init__( - self, message: str, status_code: int = None, response_data: dict = None + self, message: str, status_code: int | None = None, response_data: dict | None = None ): super().__init__(message) self.status_code = status_code @@ -29,4 +27,3 @@ def __init__( class OvermindValidationError(OvermindError): """Raised when input validation fails.""" - pass diff --git a/overmind_sdk/layers.py b/overmind/layers.py similarity index 95% rename from overmind_sdk/layers.py rename to overmind/layers.py index c9a0df1..6a12dba 100644 --- a/overmind_sdk/layers.py +++ b/overmind/layers.py @@ -1,6 +1,7 @@ -from overmind_sdk.client import get_layers_client, OvermindLayersClient -from overmind_sdk.models import LayerResponse -from typing import Sequence +from collections.abc import Sequence + +from overmind.client import OvermindLayersClient, get_layers_client +from overmind.models import LayerResponse class GenericOvermindLayer: diff --git a/overmind/models.py b/overmind/models.py new file mode 100644 index 0000000..64cc015 --- /dev/null +++ b/overmind/models.py @@ -0,0 +1,67 @@ +""" +Pydantic models for the Overmind client. +""" + +import io +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, Field +from rich.console import Console + + +class ReadableBaseModel(BaseModel): + """Base model with a readable __repr__ method for better display in Jupyter notebooks.""" + + def __repr__(self) -> str: + """ + Generate a rich-formatted string representation for the terminal. + + This is called by the Python REPL when you inspect an object. + """ + string_buffer = io.StringIO() + console = Console(file=string_buffer, force_terminal=True) + console.print(self) + return string_buffer.getvalue() + + +class AgentCreateRequest(ReadableBaseModel): + """Model for creating a new agent.""" + + agent_id: str = Field(..., description="Unique identifier for the agent") + agent_model: str | None = Field(None, description="The AI model to use (e.g., 'gpt-5-mini')") + agent_description: str | None = Field(None, description="Description of the agent") + stats: dict[str, Any] | None = Field(default={}, description="Agent statistics") + parameters: dict[str, Any] | None = Field(default={}, description="Agent parameters") + + +class AgentUpdateRequest(ReadableBaseModel): + """Model for updating an existing agent.""" + + agent_id: str = Field(..., description="Unique identifier for the agent") + agent_model: str | None = Field(None, description="The AI model to use") + agent_description: str | None = Field(None, description="Description of the agent") + stats: dict[str, Any] | None = Field(None, description="Agent statistics") + parameters: dict[str, Any] | None = Field(None, description="Agent parameters") + + +class AgentResponse(ReadableBaseModel): + """Model for agent response data.""" + + agent_id: str + agent_model: str | None + agent_description: str | None + stats: dict[str, Any] | None + parameters: dict[str, Any] | None + business_id: str + created_at: datetime | None + updated_at: datetime | None + + +class LayerResponse(BaseModel): + """Model for invocation response data.""" + + policy_results: dict[str, Any] + overall_policy_outcome: str + processed_data: str | None + span_context: dict[str, Any] diff --git a/overmind/tracing.py b/overmind/tracing.py new file mode 100644 index 0000000..9c77001 --- /dev/null +++ b/overmind/tracing.py @@ -0,0 +1,594 @@ +""" +Overmind SDK tracing. + +Provides SDK initialization, provider auto-instrumentation, and helpers +(decorators and a context manager) for creating custom OpenTelemetry spans. +""" + +import importlib.metadata +import importlib.util +import inspect +import logging +import os +from collections.abc import Callable +from contextlib import contextmanager +from enum import Enum +from functools import wraps +from typing import Any + +from opentelemetry import trace +from opentelemetry.context import attach, get_value, set_value +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from opentelemetry.semconv_ai import SpanAttributes +from opentelemetry.trace import Status, StatusCode + +from overmind.utils.api_settings import get_api_settings +from overmind.utils.serializers import serialize + +logger = logging.getLogger(__name__) + +try: + _SDK_VERSION = importlib.metadata.version("overmind") +except importlib.metadata.PackageNotFoundError: + _SDK_VERSION = "unknown" + +_strict_mode = os.environ.get("OVERMIND_STRICT_MODE", "false").lower() == "true" + +# Global state to track initialization +_initialized = False +_tracer: trace.Tracer | None = None +_providers: set[str] = set() + + +# --------------------------------------------------------------------------- +# Provider auto-instrumentation +# --------------------------------------------------------------------------- + + +def enable_agno(): + name, module = "agno", "agno" + global _providers + if name in _providers: + logger.debug("%s already enabled", name) + return + + if importlib.util.find_spec(module) is None: + if _strict_mode: + raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") + logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) + return + + from opentelemetry.instrumentation.agno import AgnoInstrumentor + + AgnoInstrumentor().instrument() + _providers.add(name) + logger.info("%s instrumentation enabled", name) + + +def enable_openai(): + name, module = "openai", "openai" + global _providers + if name in _providers: + logger.debug("%s already enabled", name) + return + + if importlib.util.find_spec(module) is None: + if _strict_mode: + raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") + logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) + return + + from opentelemetry.instrumentation.openai import OpenAIInstrumentor + + OpenAIInstrumentor().instrument() + + _providers.add(name) + logger.info("%s instrumentation enabled", name) + + +def enable_anthropic(): + name, module = "anthropic", "anthropic" + global _providers + if name in _providers: + logger.debug("%s already enabled", name) + return + + if importlib.util.find_spec(module) is None: + if _strict_mode: + raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") + logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) + return + + from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor + + AnthropicInstrumentor().instrument() + + _providers.add(name) + logger.info("%s instrumentation enabled", name) + + +def enable_google_genai(): + name, module = "google", "google.genai" + + global _providers + if name in _providers: + logger.debug("%s already enabled", name) + return + + if importlib.util.find_spec(module) is None: + module = module.replace(".", "-") + if _strict_mode: + raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") + logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) + return + + from opentelemetry.instrumentation.google_generativeai import GoogleGenerativeAiInstrumentor + + GoogleGenerativeAiInstrumentor().instrument() + + _providers.add(name) + logger.info("%s instrumentation enabled", name) + + +def enable_tracing(providers: list[str]): + if providers == []: + # if no providers are provided, enable all supported providers + providers = ["openai", "anthropic", "google", "agno"] + + logger.info("Enabling tracing for providers: %s", providers) + if "agno" in providers: + enable_agno() + if "openai" in providers: + enable_openai() + if "anthropic" in providers: + enable_anthropic() + if "google" in providers: + enable_google_genai() + + +def _span_processor_on_start(span: trace.Span, parent_context: trace.Context | None = None): + if value := get_value("workflow_name"): + span.set_attribute(SpanAttributes.TRACELOOP_WORKFLOW_NAME, str(value)) + + +# --------------------------------------------------------------------------- +# SDK initialization +# --------------------------------------------------------------------------- + + +def init( + overmind_api_key: str | None = None, + *, + service_name: str | None = None, + environment: str | None = None, + providers: list[str] | None = None, + overmind_base_url: str | None = None, +): + """ + Initialize the Overmind SDK for automatic monitoring. + + Example: + import overmind + overmind.init(service_name="my-backend", environment="production", providers=["openai", "anthropic", "google", "agno"]) + + Args: + overmind_api_key: Your Overmind API key. If not provided, uses OVERMIND_API_KEY env var. + service_name: Name of your service (appears in traces). Defaults to OVERMIND_SERVICE_NAME + env var or "unknown-service". + environment: Environment name (e.g., "production", "staging"). Defaults to + OVERMIND_ENVIRONMENT env var or "development". + providers: List of providers to trace. Supported values: "openai", "anthropic", "google", "agno". + overmind_base_url: Base URL for traces. If not provided, uses OVERMIND_API_URL env var. + """ + global _initialized, _tracer + + if providers is None: + providers = [] + + if _initialized: + # user can call init again with different providers, so we should not skip + # there is no such thing as remove initialization + logger.debug("Overmind SDK already initialized, reinitializing with providers: %s", providers) + enable_tracing(providers) + return + + # Resolve service name and environment + service_name = ( + service_name or os.environ.get("OVERMIND_SERVICE_NAME") or os.environ.get("SERVICE_NAME") or "unknown-service" + ) + environment = ( + environment or os.environ.get("OVERMIND_ENVIRONMENT") or os.environ.get("ENVIRONMENT") or "development" + ) + + overmind_api_key, overmind_base_url = get_api_settings(overmind_api_key, overmind_base_url) + + endpoint = f"{overmind_base_url}/api/v1/traces" + + # Configure OpenTelemetry Provider with rich resource attributes + resource = Resource.create({ + "service.name": service_name, + "service.version": os.environ.get("SERVICE_VERSION", "unknown"), + "deployment.environment": environment, + "overmind.sdk.name": "overmind-python", + "overmind.sdk.version": _SDK_VERSION, + }) + + provider = TracerProvider(resource=resource) + + # Configure OTLP Exporter + headers = {"X-API-Token": overmind_api_key} + + otlp_exporter = OTLPSpanExporter(endpoint=endpoint, headers=headers) + + span_processor = BatchSpanProcessor(otlp_exporter) + provider.add_span_processor(span_processor) + span_processor.on_start = _span_processor_on_start + + # Set global Trace Provider + trace.set_tracer_provider(provider) + + # Store tracer for custom spans + _tracer = trace.get_tracer("overmind", _SDK_VERSION) + enable_tracing(providers) + + _initialized = True + logger.info("Overmind SDK initialized: service=%s, environment=%s", service_name, environment) + + +def get_tracer() -> trace.Tracer: + """ + Get the Overmind tracer for creating custom spans. + + Example: + tracer = overmind.get_tracer() + with tracer.start_as_current_span("my-operation") as span: + span.set_attribute("user.id", user_id) + # ... your code ... + + Returns: + OpenTelemetry Tracer instance. + + Raises: + RuntimeError: If SDK not initialized. + """ + if not _initialized or _tracer is None: + raise RuntimeError("Overmind SDK not initialized. Call overmind.init() first.") + return _tracer + + +# --------------------------------------------------------------------------- +# Span attribute helpers +# --------------------------------------------------------------------------- + + +def set_user(user_id: str, email: str | None = None, username: str | None = None) -> None: + """ + Associate current trace with a user (like Sentry's set_user). + + Call this in your request handler to tag traces with user info. + + Example: + @app.middleware("http") + async def add_user_context(request: Request, call_next): + if request.state.user: + overmind.set_user(user_id=request.state.user.id) + return await call_next(request) + + Args: + user_id: Unique user identifier. + email: Optional user email. + username: Optional username. + """ + span = trace.get_current_span() + if span.is_recording(): + span.set_attribute("user.id", user_id) + if email: + span.set_attribute("user.email", email) + if username: + span.set_attribute("user.username", username) + + +def set_tag(key: str, value) -> None: + """ + Add a custom tag to the current span. + + Accepts str, int, float, bool, or list[str] values. Other types are + coerced to str automatically. + + Example: + overmind.set_tag("feature.flag", "new-checkout-flow") + overmind.set_tag("iteration", 3) + overmind.set_tag("score", 85.2) + """ + span = trace.get_current_span() + if not span.is_recording(): + return + if isinstance(value, (str, int, float, bool)): + span.set_attribute(key, value) + elif value is None: + span.set_attribute(key, "") + elif isinstance(value, (list, tuple)) and all(isinstance(v, str) for v in value): + span.set_attribute(key, list(value)) + else: + span.set_attribute(key, str(value)) + + +def capture_exception(exception: Exception) -> None: + """ + Record an exception on the current span. + + Example: + try: + risky_operation() + except Exception as e: + overmind.capture_exception(e) + raise + + Args: + exception: The exception to record. + """ + span = trace.get_current_span() + if span.is_recording(): + span.record_exception(exception) + span.set_status(trace.Status(trace.StatusCode.ERROR, str(exception))) + + +def set_workflow_name(workflow_name: str) -> None: + attach(set_value("workflow_name", workflow_name)) + + +def set_agent_name(agent_name: str) -> None: + attach(set_value("agent_name", agent_name)) + + +def set_conversation_id(conversation_id: str): + attach(set_value("conversation_id", conversation_id)) + + +# --------------------------------------------------------------------------- +# Span types and decorators +# --------------------------------------------------------------------------- + + +class SpanType(str, Enum): + FUNCTION = "function" + ENTRY_POINT = "entry_point" + WORKFLOW = "workflow" + TOOL = "tool" + + +_SKIP_INPUT_TYPES = ( + "Console", "Progress", "Live", "Table", "Panel", + "TracerProvider", "Tracer", "Span", +) + + +def _should_skip_value(value: Any) -> bool: + type_name = type(value).__name__ + return type_name in _SKIP_INPUT_TYPES + + +def _prepare_for_otel(value: Any) -> Any: + if isinstance(value, (str, int, float, bool, type(None))): + return value + + if _should_skip_value(value): + return f"<{type(value).__name__}>" + + if hasattr(value, "model_dump"): + try: + return value.model_dump() + except Exception: + return str(value) + + if isinstance(value, (dict, list, tuple)): + return value + + if isinstance(value, (set, frozenset)): + return list(value) + + from pathlib import PurePath + if isinstance(value, PurePath): + return str(value) + + return str(value) + + +def _safe_set_attribute(otel_span, key: str, value: Any) -> None: + """Set a span attribute, coercing the value to an OTel-compatible type.""" + if isinstance(value, (str, int, float, bool)): + otel_span.set_attribute(key, value) + elif value is None: + otel_span.set_attribute(key, "") + elif isinstance(value, (list, tuple)): + if all(isinstance(v, str) for v in value): + otel_span.set_attribute(key, list(value)) + else: + otel_span.set_attribute(key, serialize(value)) + else: + otel_span.set_attribute(key, str(value)) + + +def observe(span_name: str | None = None, type: SpanType = SpanType.FUNCTION): + """ + Decorator that automatically traces function execution with OpenTelemetry. + + Captures function inputs and outputs as span attributes in OTEL style. + Works with both synchronous and asynchronous functions. + """ + + def decorator(func: Callable) -> Callable: + name = span_name or func.__name__ + + is_async = inspect.iscoroutinefunction(func) + + if is_async: + + @wraps(func) + async def async_wrapper(*args, **kwargs): + tracer = get_tracer() + + sig = inspect.signature(func) + param_names = list(sig.parameters.keys()) + is_method = len(param_names) > 0 and param_names[0] in ("self", "cls") + start_idx = 1 if is_method else 0 + + with tracer.start_as_current_span(name) as otel_span: + try: + otel_span.set_attribute("name", name) + otel_span.set_attribute("type", type.value) + + inputs = {} + for i, arg in enumerate(args[start_idx:], start=start_idx): + if _should_skip_value(arg): + continue + param_name = param_names[i] if i < len(param_names) else f"arg_{i}" + inputs[param_name] = _prepare_for_otel(arg) + + for key, value in kwargs.items(): + if _should_skip_value(value): + continue + inputs[key] = _prepare_for_otel(value) + + otel_span.set_attribute("inputs", serialize(inputs)) + + result = await func(*args, **kwargs) + + output = _prepare_for_otel(result) + otel_span.set_attribute("outputs", serialize(output)) + + otel_span.set_status(Status(StatusCode.OK)) + + return result + + except Exception as e: + otel_span.record_exception(e) + otel_span.set_status(Status(StatusCode.ERROR, str(e))) + raise + + return async_wrapper + else: + + @wraps(func) + def sync_wrapper(*args, **kwargs): + tracer = get_tracer() + + sig = inspect.signature(func) + param_names = list(sig.parameters.keys()) + is_method = len(param_names) > 0 and param_names[0] in ("self", "cls") + start_idx = 1 if is_method else 0 + + with tracer.start_as_current_span(name) as otel_span: + try: + otel_span.set_attribute("name", name) + otel_span.set_attribute("type", type.value) + + inputs = {} + for i, arg in enumerate(args[start_idx:], start=start_idx): + if _should_skip_value(arg): + continue + param_name = param_names[i] if i < len(param_names) else f"arg_{i}" + inputs[param_name] = _prepare_for_otel(arg) + + for key, value in kwargs.items(): + if _should_skip_value(value): + continue + inputs[key] = _prepare_for_otel(value) + + otel_span.set_attribute("inputs", serialize(inputs)) + + result = func(*args, **kwargs) + + output = _prepare_for_otel(result) + otel_span.set_attribute("outputs", serialize(output)) + + otel_span.set_status(Status(StatusCode.OK)) + + return result + + except Exception as e: + otel_span.record_exception(e) + otel_span.set_status(Status(StatusCode.ERROR, str(e))) + raise + + return sync_wrapper + + return decorator + + +@contextmanager +def start_span(name: str, span_type: SpanType = SpanType.FUNCTION, attributes: dict[str, Any] | None = None): + """Context manager that creates a child span under the current trace. + + Use this for explicit span creation in loops or conditional blocks + where a decorator isn't practical. + + Example:: + + for i in range(iterations): + with start_span("iteration", attributes={"iteration": i, "score": score}): + # ... iteration work ... + set_tag("decision", "keep") + """ + tracer = get_tracer() + with tracer.start_as_current_span(name) as otel_span: + otel_span.set_attribute("name", name) + otel_span.set_attribute("type", span_type.value) + if attributes: + for key, value in attributes.items(): + _safe_set_attribute(otel_span, key, value) + try: + yield otel_span + except Exception as e: + otel_span.record_exception(e) + otel_span.set_status(Status(StatusCode.ERROR, str(e))) + raise + else: + otel_span.set_status(Status(StatusCode.OK)) + + +def conversation(conversation_id: str): + """Decorator that sets a conversation ID in the current context.""" + + def decorator(fn: Callable) -> Callable: + if inspect.iscoroutinefunction(fn): + + @wraps(fn) + async def async_wrapper(*args, **kwargs): + set_conversation_id(conversation_id) + return await fn(*args, **kwargs) + + return async_wrapper + else: + + @wraps(fn) + def sync_wrapper(*args, **kwargs): + set_conversation_id(conversation_id) + return fn(*args, **kwargs) + + return sync_wrapper + + return decorator + + +def function(name: str | None = None): + """Decorator that traces a function span.""" + return observe(span_name=name, type=SpanType.FUNCTION) + + +def entry_point(name: str | None = None): + """Decorator that traces an entry point span.""" + return observe(span_name=name, type=SpanType.ENTRY_POINT) + + +def workflow(name: str | None = None): + """Decorator that traces a workflow span.""" + return observe(span_name=name, type=SpanType.WORKFLOW) + + +def tool(name: str | None = None): + """Decorator that traces a tool span.""" + return observe(span_name=name, type=SpanType.TOOL) diff --git a/overmind_sdk/utils/__init__.py b/overmind/utils/__init__.py similarity index 100% rename from overmind_sdk/utils/__init__.py rename to overmind/utils/__init__.py diff --git a/overmind_sdk/utils/api_settings.py b/overmind/utils/api_settings.py similarity index 100% rename from overmind_sdk/utils/api_settings.py rename to overmind/utils/api_settings.py diff --git a/overmind_sdk/utils/dump_logs.py b/overmind/utils/dump_logs.py similarity index 80% rename from overmind_sdk/utils/dump_logs.py rename to overmind/utils/dump_logs.py index 583f360..5a0c269 100644 --- a/overmind_sdk/utils/dump_logs.py +++ b/overmind/utils/dump_logs.py @@ -1,16 +1,17 @@ -from collections.abc import Iterator +import csv import json +from collections.abc import Iterator from pathlib import Path -from overmind_sdk.tracing import init, get_tracer -from tqdm import tqdm + from opentelemetry import trace -from typing import Dict, Optional -from pydantic import BaseModel, ConfigDict, Field -import csv from opentelemetry.trace import SpanKind +from pydantic import BaseModel, ConfigDict, Field +from tqdm import tqdm + +from overmind.tracing import get_tracer, init -def get_log_item_model(mapping: Dict[str, str] = None): +def get_log_item_model(mapping: dict[str, str] | None = None): if mapping is None: mapping = {} @@ -25,29 +26,29 @@ class LogItem(BaseModel): end_time: int = Field(..., alias=get_field("end_time")) # custom IDs (must be valid 32/16 hex chars) - trace_state: Optional[str] = Field(default=None, alias=get_field("trace_state"), max_length=32, min_length=16) - trace_id: Optional[str] = Field(default=None, alias=get_field("trace_id"), max_length=32, min_length=16) - span_id: Optional[str] = Field(default=None, alias=get_field("span_id"), max_length=32, min_length=16) - parent_span_id: Optional[str] = Field( + trace_state: str | None = Field(default=None, alias=get_field("trace_state"), max_length=32, min_length=16) + trace_id: str | None = Field(default=None, alias=get_field("trace_id"), max_length=32, min_length=16) + span_id: str | None = Field(default=None, alias=get_field("span_id"), max_length=32, min_length=16) + parent_span_id: str | None = Field( default=None, alias=get_field("parent_span_id"), max_length=32, min_length=16, ) - name: Optional[str] = Field(default="log-ingestion-service", alias=get_field("name")) + name: str | None = Field(default="log-ingestion-service", alias=get_field("name")) kind: int = Field(default=2, alias=get_field("kind")) status_code: int = Field(default=0, alias=get_field("status_code")) status_message: str = Field(default="", alias=get_field("status_message")) - extra_attributes: Dict[str, str] = Field(..., default_factory=dict, alias=get_field("extra_attributes")) + extra_attributes: dict[str, str] = Field(..., default_factory=dict, alias=get_field("extra_attributes")) model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") return LogItem -def process_log_item(item: dict, mapping: Dict[str, str]): +def process_log_item(item: dict, mapping: dict[str, str]): log_model = get_log_item_model(mapping) log_item = log_model.model_validate(item, by_alias=True) @@ -93,18 +94,16 @@ def load_from_jsonl(filepath: str) -> Iterator[dict]: def load_from_json(filepath: str) -> Iterator[dict]: with open(filepath, "r") as f: data = json.load(f) - for item in data: - yield item + yield from data def load_from_csv(filepath: str) -> Iterator[dict]: with open(filepath, "r", newline="") as f: reader = csv.DictReader(f) - for row in reader: - yield row + yield from reader -def ingest_logs(filepath: str, mapping: Dict[str, str], **kwargs): +def ingest_logs(filepath: str, mapping: dict[str, str], **kwargs): if kwargs.get("overmind_api_key"): init(overmind_api_key=kwargs.get("overmind_api_key")) else: diff --git a/overmind/utils/serializers.py b/overmind/utils/serializers.py new file mode 100644 index 0000000..65a9e1c --- /dev/null +++ b/overmind/utils/serializers.py @@ -0,0 +1,34 @@ +import json +import logging +from pathlib import PurePath + +_MAX_ATTR_LEN = 32_000 + +logger = logging.getLogger(__name__) + +def _default_serializer(obj): + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (set, frozenset)): + return list(obj) + if isinstance(obj, bytes): + return obj.hex() + if hasattr(obj, "model_dump"): + try: + return obj.model_dump() + except Exception: + logger.exception("Error serializing object") + if hasattr(obj, "__dict__"): + return {k: v for k, v in obj.__dict__.items() if not k.startswith("_")} + return repr(obj) + + +def serialize(obj) -> str: + try: + raw = json.dumps(obj, default=_default_serializer, ensure_ascii=False) + except (TypeError, ValueError, OverflowError): + raw = repr(obj) + + if len(raw) > _MAX_ATTR_LEN: + return raw[:_MAX_ATTR_LEN] + "…[truncated]" + return raw diff --git a/overmind_sdk/client.py b/overmind_sdk/client.py deleted file mode 100644 index 3b1deed..0000000 --- a/overmind_sdk/client.py +++ /dev/null @@ -1,259 +0,0 @@ -""" -Main Overmind client implementation. -""" - -import os -from functools import lru_cache -from typing import Any, Dict, List, Optional, Sequence -from urllib.parse import urljoin - -import requests - -from .exceptions import OvermindAPIError, OvermindAuthenticationError, OvermindError -from .models import LayerResponse -from .policies import PoliciesClient -from .utils.api_settings import get_api_settings -from .utils.serializers import serialize -from .models import ProxyRunResponse - -# Mapping of common environment variables to provider parameter names -COMMON_ENV_VARS = { - "OPENAI_API_KEY": "api_key", -} - - -class ClientPathProxy: - """ - Proxy object that enables dynamic method chaining for client paths. - - This allows for syntax like: client.openai.chat.completions.create(...) - """ - - def __init__(self, client, path_parts: List[str]): - self.client = client - self.path_parts = path_parts - - def __getattr__(self, name: str): - """Add the attribute name to the path and return self for chaining.""" - return ClientPathProxy(self.client, self.path_parts + [name]) - - def __call__(self, *args, **kwargs): - """ - When called, construct the full path and invoke the provider. - - Args: - *args: Positional arguments (not used in this implementation) - **kwargs: Keyword arguments for the provider call - - Returns: - The result from the provider invocation - """ - if not self.path_parts: - raise OvermindError("No method path specified") - - # Construct the full client path - client_path = ".".join(self.path_parts) - - input_policies = kwargs.pop("input_policies", None) - output_policies = kwargs.pop("output_policies", None) - - # Invoke the provider through the Overmind API - return self.client.invoke( - client_path=client_path, - client_call_params=serialize(kwargs), - input_policies=input_policies, - output_policies=output_policies, - ) - - -class OvermindClient: - """ - Main client for interacting with the Overmind API. - - This client provides: - - Dynamic provider access (e.g., client.openai.chat.completions.create) - - Policy management via client.policies.{methods} - """ - - def __init__( - self, - overmind_api_key: Optional[str] = None, - base_url: Optional[str] = None, - **provider_parameters: Dict[str, Any], - ): - """ - Initialize the Overmind client. - - Args: - overmind_api_key: Your Overmind API key for authentication. If not provided, - will try to use OVERMIND_API_KEY environment variable. - base_url: Base URL of the Overmind API server - **provider_parameters: Provider-specific credentials (e.g., openai_api_key) - - Raises: - OvermindError: If no API key is provided and OVERMIND_API_KEY environment variable is not set - """ - self.overmind_api_key, self.base_url = get_api_settings(overmind_api_key, base_url) - - # Start with provided provider parameters - self.provider_parameters = provider_parameters.copy() if provider_parameters else {} - - # Add common environment variables if they exist and aren't already in provider_parameters - for env_var, param_name in COMMON_ENV_VARS.items(): - env_value = os.getenv(env_var) - if env_value and param_name not in self.provider_parameters: - self.provider_parameters[param_name] = env_value - - self.session = requests.Session() - self.session.headers.update( - { - "X-API-Token": self.overmind_api_key, - "Content-Type": "application/json", - } - ) - - # Initialize sub-clients - self.policies = PoliciesClient(self) - - # Cache for provider proxies - self._provider_proxies = {} - - def __getattr__(self, name: str): - """Enable dynamic provider access (e.g., client.openai).""" - if name in self._provider_proxies: - return self._provider_proxies[name] - - # Create a new proxy for this provider - proxy = ClientPathProxy(self, [name]) - self._provider_proxies[name] = proxy - return proxy - - def _make_request( - self, - method: str, - endpoint: str, - data: Optional[Dict[str, Any]] = None, - params: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: - """ - Make an HTTP request to the Overmind API. - - Args: - method: HTTP method (GET, POST, PUT, DELETE) - endpoint: API endpoint path - data: Request body data - params: Query parameters - - Returns: - Response data as dictionary - - Raises: - OvermindAuthenticationError: If authentication fails - OvermindAPIError: If the API returns an error - """ - url = urljoin(f"{self.base_url}/api/v1/", endpoint) - - try: - response = self.session.request(method=method, url=url, json=data, params=params) - - if response.status_code == 401: - raise OvermindAuthenticationError("Invalid Overmind API key") - - if response.status_code >= 400: - error_data = response.json() if response.content else {} - raise OvermindAPIError( - message=error_data.get("detail", f"HTTP {response.status_code}"), - status_code=response.status_code, - response_data=error_data, - ) - - return response.json() if response.content else {} - - except requests.exceptions.RequestException as e: - raise OvermindError(f"Request failed: {str(e)}") - - def invoke( - self, - client_path: str, - client_call_params: Dict[str, Any], - agent_id: str = "default_agent", - client_init_params: Optional[Dict[str, Any]] = None, - input_policies: Optional[List[str]] = None, - output_policies: Optional[List[str]] = None, - ) -> ProxyRunResponse: - """ - Invoke an AI provider through the Overmind API. - - Args: - client_path: Provider path (e.g., "openai.chat.completions.create") - client_call_params: Parameters for the provider call - client_init_params: Parameters for provider client initialization (overrides stored parameters) - input_policies: Input policies to apply - output_policies: Output policies to apply - - Returns: - ProxyRunResponse object - """ - # Use provided client_init_params or fall back to stored provider_parameters - init_params = client_init_params or self.provider_parameters - - payload = { - "client_call_params": client_call_params, - "client_init_params": init_params, - "input_policies": input_policies, - "output_policies": output_policies, - } - - response_data = self._make_request("POST", f"proxy/run/{client_path}", data=payload) - - return ProxyRunResponse(**response_data) - - -class OvermindLayersClient: - def __init__( - self, - overmind_api_key: Optional[str] = None, - base_url: Optional[str] = None, - traces_base_url: Optional[str] = None, - ): - self.overmind_api_key, self.base_url = get_api_settings(overmind_api_key, base_url) - self.session = requests.Session() - self.session.headers.update( - { - "X-API-Token": self.overmind_api_key, - "Content-Type": "application/json", - } - ) - - def run_layer( - self, input_data: str, policies: Sequence[str | dict], layer_position: str, **kwargs - ) -> LayerResponse: - """ - Run a layer of the Overmind API. - """ - payload = { - "input_data": input_data, - "policies": policies, - "layer_position": layer_position, - "kwargs": kwargs, - } - - response_data = self.session.request("POST", f"{self.base_url}/api/v1/layers/run", json=payload) - - if response_data.status_code != 200: - raise OvermindAPIError( - message=response_data.text, - status_code=response_data.status_code, - response_data=response_data.json(), - ) - - return LayerResponse(**response_data.json()) - - -@lru_cache -def get_layers_client( - overmind_api_key: Optional[str] = None, - base_url: Optional[str] = None, - traces_base_url: Optional[str] = None, -): - return OvermindLayersClient(overmind_api_key, base_url, traces_base_url) diff --git a/overmind_sdk/filexporter.py b/overmind_sdk/filexporter.py deleted file mode 100644 index ed8678e..0000000 --- a/overmind_sdk/filexporter.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -import json -import threading -from pathlib import Path -from typing import IO, Sequence - -from google.protobuf.json_format import MessageToDict -from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans -from opentelemetry.sdk.trace import ReadableSpan -from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult - - -class FileSpanExporter(SpanExporter): - def __init__( - self, - file_path: str | Path | IO[str], - ) -> None: - self.file_path = Path(file_path) if isinstance(file_path, str) else file_path - self._lock = threading.Lock() - self._file: IO[str] | None = None - - def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: - with self._lock: - if not self._file: - if isinstance(self.file_path, Path): - self._file = self.file_path.open('a', encoding='utf-8') - else: - self._file = self.file_path - encoded = encode_spans(spans) - data = MessageToDict(encoded, preserving_proto_field_name=True) - self._file.write(json.dumps(data) + '\n') - self._file.flush() - return SpanExportResult.SUCCESS - - def force_flush(self, timeout_millis: int = 30000) -> bool: - return True - - def shutdown(self) -> None: - with self._lock: - if self._file: - self._file.flush() - if self._file is not self.file_path: - self._file.close() diff --git a/overmind_sdk/models.py b/overmind_sdk/models.py deleted file mode 100644 index d5c8aa7..0000000 --- a/overmind_sdk/models.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Pydantic models for the Overmind client. -""" - -from datetime import datetime -from typing import Any, Dict, List, Optional -from pydantic import BaseModel, Field, model_validator -from .utils.formatters import summarize_proxy_run -from rich.console import Console -import io - - -class ReadableBaseModel(BaseModel): - """Base model with a readable __repr__ method for better display in Jupyter notebooks.""" - - def __repr__(self) -> str: - """ - Generate a rich-formatted string representation for the terminal. - - This is called by the Python REPL when you inspect an object. - """ - # Create a Rich Console that captures output to a string - string_buffer = io.StringIO() - console = Console(file=string_buffer, force_terminal=True) - console.print(self) - return string_buffer.getvalue() - - -class AgentCreateRequest(ReadableBaseModel): - """Model for creating a new agent.""" - - agent_id: str = Field(..., description="Unique identifier for the agent") - agent_model: Optional[str] = Field(None, description="The AI model to use (e.g., 'gpt-5-mini')") - agent_description: Optional[str] = Field(None, description="Description of the agent") - input_policies: Optional[List[str]] = Field(default=[], description="List of input policy IDs") - output_policies: Optional[List[str]] = Field(default=[], description="List of output policy IDs") - stats: Optional[Dict[str, Any]] = Field(default={}, description="Agent statistics") - parameters: Optional[Dict[str, Any]] = Field(default={}, description="Agent parameters") - - -class AgentUpdateRequest(ReadableBaseModel): - """Model for updating an existing agent.""" - - agent_id: str = Field(..., description="Unique identifier for the agent") - agent_model: Optional[str] = Field(None, description="The AI model to use") - agent_description: Optional[str] = Field(None, description="Description of the agent") - input_policies: Optional[List[str]] = Field(None, description="List of input policy IDs") - output_policies: Optional[List[str]] = Field(None, description="List of output policy IDs") - stats: Optional[Dict[str, Any]] = Field(None, description="Agent statistics") - parameters: Optional[Dict[str, Any]] = Field(None, description="Agent parameters") - - -class AgentResponse(ReadableBaseModel): - """Model for agent response data.""" - - agent_id: str - agent_model: Optional[str] - agent_description: Optional[str] - input_policies: Optional[List[str]] - output_policies: Optional[List[str]] - stats: Optional[Dict[str, Any]] - parameters: Optional[Dict[str, Any]] - business_id: str - created_at: Optional[datetime] - updated_at: Optional[datetime] - - -class PolicyCreateRequest(ReadableBaseModel): - """Model for creating a new policy.""" - - policy_id: str = Field(..., description="Unique identifier for the policy") - policy_description: str = Field(..., description="Description of the policy") - parameters: Dict[str, Any] = Field(..., description="Policy parameters") - policy_template: str = Field(..., description="Policy template") - is_input_policy: bool = Field(..., description="Whether this is an input policy") - is_output_policy: bool = Field(..., description="Whether this is an output policy") - stats: Optional[Dict[str, Any]] = Field(default={}, description="Policy statistics") - - @model_validator(mode="after") - def validate_policy_type(self): - """Ensure at least one of is_input_policy or is_output_policy is True.""" - if not self.is_input_policy and not self.is_output_policy: - raise ValueError("At least one of is_input_policy or is_output_policy must be True") - return self - - -class PolicyUpdateRequest(ReadableBaseModel): - """Model for updating an existing policy.""" - - policy_id: str = Field(..., description="Unique identifier for the policy") - policy_description: Optional[str] = Field(None, description="Description of the policy") - parameters: Optional[Dict[str, Any]] = Field(None, description="Policy parameters") - policy_template: Optional[str] = Field(None, description="Policy template") - is_input_policy: Optional[bool] = Field(None, description="Whether this is an input policy") - is_output_policy: Optional[bool] = Field(None, description="Whether this is an output policy") - stats: Optional[Dict[str, Any]] = Field(None, description="Policy statistics") - - -class PolicyResponse(ReadableBaseModel): - """Model for policy response data.""" - - policy_id: str - policy_description: str - parameters: Dict[str, Any] - policy_template: str - stats: Dict[str, Any] - is_input_policy: bool - is_output_policy: bool - created_at: Optional[datetime] - updated_at: Optional[datetime] - is_built_in: Optional[bool] = False - - -class LayerResponse(BaseModel): - """Model for invocation response data.""" - - policy_results: Dict[str, Any] - overall_policy_outcome: str - processed_data: Optional[str] - span_context: Dict[str, Any] - - -class ProxyRunResponse(ReadableBaseModel): - """Model for proxy run response data.""" - - llm_client_response: Dict[str, Any] - input_layer_results: Dict[str, Any] - output_layer_results: Dict[str, Any] - processed_output: Any - processed_input: Any - span_context: Dict[str, Any] - - def summary(self) -> None: - summarize_proxy_run(self) diff --git a/overmind_sdk/policies.py b/overmind_sdk/policies.py deleted file mode 100644 index d3efa78..0000000 --- a/overmind_sdk/policies.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Policies sub-client for Overmind API. -""" - -from typing import Any, Dict, List, Optional, Union - -from .models import PolicyCreateRequest, PolicyResponse, PolicyUpdateRequest - - -class PoliciesClient: - """ - Sub-client for managing policies in the Overmind API. - """ - - def __init__(self, parent_client): - self._client = parent_client - - def create( - self, - policy_id: str, - policy_template: str, - policy_description: Optional[str] = None, - parameters: Optional[Dict[str, Any]] = None, - is_input_policy: Optional[bool] = True, - is_output_policy: Optional[bool] = True, - stats: Optional[Dict[str, Any]] = None, - *, - policy_data: Optional[Union[PolicyCreateRequest, Dict[str, Any]]] = None, - ) -> PolicyResponse: - """ - Create a new policy. - - Args: - policy_id: Unique identifier for the policy - policy_description: Description of the policy - parameters: Policy parameters - policy_template: Policy template to use - is_input_policy: Whether this is an input policy - is_output_policy: Whether this is an output policy - stats: Policy statistics - policy_data: Alternative: pass a complete PolicyCreateRequest or dict (for backward compatibility) - """ - if policy_data is not None: - if isinstance(policy_data, dict): - policy_data = PolicyCreateRequest(**policy_data) - elif isinstance(policy_data, PolicyCreateRequest): - pass - else: - raise TypeError("policy_data must be a dict or PolicyCreateRequest") - else: - policy_data = PolicyCreateRequest( - policy_id=policy_id, - policy_description=policy_description, - parameters=parameters, - policy_template=policy_template, - is_input_policy=is_input_policy, - is_output_policy=is_output_policy, - stats=stats or {}, - ) - - response_data = self._client._make_request( - "PUT", "policies/add_policy", data=policy_data.model_dump() - ) - return response_data - - def list(self, policy_type: Optional[str] = None) -> List[PolicyResponse]: - """ - List all policies with optional filtering by type. - - Args: - policy_type: Optional filter to show only input or output policies - """ - params = {"policy_type": policy_type} if policy_type else None - response_data = self._client._make_request( - "GET", "policies/list_policies", params=params - ) - return [PolicyResponse(**policy) for policy in response_data] - - def get(self, policy_id: str) -> PolicyResponse: - """ - Get a specific policy by ID. - - Args: - policy_id: The unique identifier of the policy to retrieve - """ - response_data = self._client._make_request("GET", f"policies/view/{policy_id}") - return PolicyResponse(**response_data) - - def update( - self, - policy_id: str, - policy_template: str, - policy_description: Optional[str] = None, - parameters: Optional[Dict[str, Any]] = None, - is_input_policy: Optional[bool] = None, - is_output_policy: Optional[bool] = None, - stats: Optional[Dict[str, Any]] = None, - *, - policy_data: Optional[Union[PolicyUpdateRequest, Dict[str, Any]]] = None, - ) -> Dict[str, str]: - """ - Update an existing policy. - - Args: - policy_id: Unique identifier for the policy - policy_description: Description of the policy - parameters: Policy parameters - engine: Policy engine - is_input_policy: Whether this is an input policy - is_output_policy: Whether this is an output policy - stats: Policy statistics - policy_data: Alternative: pass a complete PolicyUpdateRequest or dict (for backward compatibility) - """ - if policy_data is not None: - if isinstance(policy_data, dict): - policy_data = PolicyUpdateRequest(**policy_data) - elif isinstance(policy_data, PolicyUpdateRequest): - pass - else: - raise TypeError("policy_data must be a dict or PolicyUpdateRequest") - else: - policy_data = PolicyUpdateRequest( - policy_id=policy_id, - policy_description=policy_description, - parameters=parameters, - policy_template=policy_template, - is_input_policy=is_input_policy, - is_output_policy=is_output_policy, - stats=stats, - ) - - return self._client._make_request( - "POST", "policies/edit_policy", data=policy_data.model_dump() - ) - - def delete(self, policy_id: str) -> Dict[str, str]: - """ - Delete a policy by ID. - - Args: - policy_id: The unique identifier of the policy to delete - """ - return self._client._make_request("GET", f"policies/delete/{policy_id}") diff --git a/overmind_sdk/tracer.py b/overmind_sdk/tracer.py deleted file mode 100644 index 293a2ae..0000000 --- a/overmind_sdk/tracer.py +++ /dev/null @@ -1,309 +0,0 @@ -""" -OpenTelemetry tracing decorator for automatic function instrumentation. - -This module provides a decorator that automatically traces function calls, -capturing inputs and outputs in OpenTelemetry style. -""" - -from enum import Enum -from functools import wraps -import inspect -from typing import Any, Callable, Optional -from opentelemetry.context import attach, set_value -from opentelemetry.trace import Status, StatusCode - -from overmind_sdk.tracing import get_tracer -from overmind_sdk.utils.serializers import serialize - - -class SpanType(str, Enum): - FUNCTION = "function" - ENTRY_POINT = "entry_point" - WORKFLOW = "workflow" - TOOL = "tool" - - -def _prepare_for_otel(value: Any) -> Any: - """ - Prepare a value for inclusion in OpenTelemetry span attributes. - - Converts complex types to serializable formats while preserving simple types. - """ - # Simple types can be used directly - if isinstance(value, (str, int, float, bool, type(None))): - return value - - # For Pydantic models, convert to dict - if hasattr(value, "model_dump"): - try: - return value.model_dump() - except Exception: - return str(value) - - # For dicts, lists, tuples - return as-is (will be serialized later) - if isinstance(value, (dict, list, tuple)): - return value - - # For other types, convert to string - return str(value) - - -def observe(span_name: Optional[str] = None, type: SpanType = SpanType.FUNCTION): - """ - Decorator that automatically traces function execution with OpenTelemetry. - - Captures function inputs and outputs as span attributes in OTEL style. - Works with both synchronous and asynchronous functions. - - Args: - span_name: Optional name for the span. If not provided, uses the function name. - type: The type of span, as a SpanType enum value. - - Example: - ```python - @observe(span_name="process_data", type=SpanType.FUNCTION) - def process_data(user_id: int, data: dict): - return {"result": "success"} - - @observe() # Uses function name as span name - async def async_operation(param: str): - return await some_async_call(param) - ``` - - The decorator will: - - Create a span with the specified name (or function name) - - Set the span name and type as attributes - - Capture all function arguments as span attributes (skips self/cls for class methods) - - Capture the return value as a span attribute - - Record exceptions if they occur - - Set appropriate span status codes - """ - - def decorator(func: Callable) -> Callable: - # Determine span name - name = span_name or func.__name__ - - # Check if function is async - is_async = inspect.iscoroutinefunction(func) - - if is_async: - - @wraps(func) - async def async_wrapper(*args, **kwargs): - tracer = get_tracer() - - # Get function signature for better argument names - sig = inspect.signature(func) - bound_args = sig.bind(*args, **kwargs) - bound_args.apply_defaults() - - # Check if this is a method (has self or cls as first parameter) - param_names = list(sig.parameters.keys()) - is_method = len(param_names) > 0 and param_names[0] in ("self", "cls") - start_idx = 1 if is_method else 0 - - # Start span - with tracer.start_as_current_span(name) as span: - try: - span.set_attribute("name", name) - span.set_attribute("type", type.value) - - # Capture inputs - inputs = {} - - # Add positional arguments (skip self/cls if it's a method) - for i, arg in enumerate(args[start_idx:], start=start_idx): - if i < len(param_names): - param_name = param_names[i] - inputs[param_name] = _prepare_for_otel(arg) - else: - inputs[f"arg_{i}"] = _prepare_for_otel(arg) - - # Add keyword arguments - for key, value in kwargs.items(): - inputs[key] = _prepare_for_otel(value) - - # Set input attributes on span (serialize the entire inputs dict) - span.set_attribute("inputs", serialize(inputs)) - - # Execute function - result = await func(*args, **kwargs) - - # Capture output - output = _prepare_for_otel(result) - span.set_attribute("outputs", serialize(output)) - - # Set success status - span.set_status(Status(StatusCode.OK)) - - return result - - except Exception as e: - # Record exception - span.record_exception(e) - span.set_status(Status(StatusCode.ERROR, str(e))) - raise - - return async_wrapper - else: - - @wraps(func) - def sync_wrapper(*args, **kwargs): - tracer = get_tracer() - - # Get function signature for better argument names - sig = inspect.signature(func) - bound_args = sig.bind(*args, **kwargs) - bound_args.apply_defaults() - - # Check if this is a method (has self or cls as first parameter) - param_names = list(sig.parameters.keys()) - is_method = len(param_names) > 0 and param_names[0] in ("self", "cls") - start_idx = 1 if is_method else 0 - - # Start span - with tracer.start_as_current_span(name) as span: - try: - span.set_attribute("name", name) - span.set_attribute("type", type.value) - - # Capture inputs - inputs = {} - - # Add positional arguments (skip self/cls if it's a method) - for i, arg in enumerate(args[start_idx:], start=start_idx): - if i < len(param_names): - param_name = param_names[i] - inputs[param_name] = _prepare_for_otel(arg) - else: - inputs[f"arg_{i}"] = _prepare_for_otel(arg) - - # Add keyword arguments - for key, value in kwargs.items(): - inputs[key] = _prepare_for_otel(value) - - # Set input attributes on span (serialize the entire inputs dict) - span.set_attribute("inputs", serialize(inputs)) - - # Execute function - result = func(*args, **kwargs) - - # Capture output - output = _prepare_for_otel(result) - span.set_attribute("outputs", serialize(output)) - - # Set success status - span.set_status(Status(StatusCode.OK)) - - return result - - except Exception as e: - # Record exception - span.record_exception(e) - span.set_status(Status(StatusCode.ERROR, str(e))) - raise - - return sync_wrapper - - return decorator - - -def set_workflow_name(workflow_name: str) -> None: - attach(set_value("workflow_name", workflow_name)) - - -def set_agent_name(agent_name: str) -> None: - attach(set_value("agent_name", agent_name)) - - -def set_conversation_id(conversation_id: str): - """ - Set the conversation ID for the current context. - """ - attach(set_value("conversation_id", conversation_id)) - - -def conversation(conversation_id: str): - """ - Decorator that automatically traces a conversation with OpenTelemetry. - """ - - def decorator(fn: Callable) -> Callable: - if inspect.iscoroutinefunction(fn): - - @wraps(fn) - async def async_wrapper(*args, **kwargs): - set_conversation_id(conversation_id) - return await fn(*args, **kwargs) - - return async_wrapper - else: - - @wraps(fn) - def sync_wrapper(*args, **kwargs): - set_conversation_id(conversation_id) - return fn(*args, **kwargs) - - return sync_wrapper - - return decorator - - -def function(name: Optional[str] = None): - """ - Decorator that traces a function span. - - Args: - name: Optional span name. Defaults to the decorated function's name. - - Example: - @function(name="process_data") - def process_data(user_id: int): - ... - """ - return observe(span_name=name, type=SpanType.FUNCTION) - - -def entry_point(name: Optional[str] = None): - """ - Decorator that traces an entry point span. - - Args: - name: Optional span name. Defaults to the decorated function's name. - - Example: - @entry_point(name="api_handler") - async def handle_request(request): - ... - """ - return observe(span_name=name, type=SpanType.ENTRY_POINT) - - -def workflow(name: Optional[str] = None): - """ - Decorator that traces a workflow span. - - Args: - name: Optional span name. Defaults to the decorated function's name. - - Example: - @workflow(name="onboarding_flow") - def run_onboarding(user_id: str): - ... - """ - return observe(span_name=name, type=SpanType.WORKFLOW) - - -def tool(name: Optional[str] = None): - """ - Decorator that traces a tool span. - - Args: - name: Optional span name. Defaults to the decorated function's name. - - Example: - @tool(name="web_search") - def search(query: str): - ... - """ - return observe(span_name=name, type=SpanType.TOOL) diff --git a/overmind_sdk/tracing.py b/overmind_sdk/tracing.py deleted file mode 100644 index b1d20ae..0000000 --- a/overmind_sdk/tracing.py +++ /dev/null @@ -1,304 +0,0 @@ -import logging -import os -from opentelemetry import trace -from opentelemetry.context import get_value -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor -from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter -from opentelemetry.sdk.resources import Resource -from opentelemetry.semconv_ai import SpanAttributes -import importlib.metadata -import importlib.util - -from overmind_sdk.utils.api_settings import get_api_settings - -logger = logging.getLogger(__name__) - -try: - _SDK_VERSION = importlib.metadata.version("overmind_sdk") -except importlib.metadata.PackageNotFoundError: - _SDK_VERSION = "unknown" - -_strict_mode = os.environ.get("OVERMIND_STRICT_MODE", "false").lower() == "true" - -# Global state to track initialization -_initialized = False -_tracer: trace.Tracer | None = None -_providers: set[str] = set() - - -def enable_agno(): - name, module = "agno", "agno" - global _providers - if name in _providers: - logger.debug("%s already enabled", name) - return - - if importlib.util.find_spec(module) is None: - if _strict_mode: - raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") - logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) - return - - from opentelemetry.instrumentation.agno import AgnoInstrumentor - - AgnoInstrumentor().instrument() - _providers.add(name) - logger.info("%s instrumentation enabled", name) - - -def enable_openai(): - name, module = "openai", "openai" - global _providers - if name in _providers: - logger.debug("%s already enabled", name) - return - - if importlib.util.find_spec(module) is None: - if _strict_mode: - raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") - logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) - return - - from opentelemetry.instrumentation.openai import OpenAIInstrumentor - - OpenAIInstrumentor().instrument() - - _providers.add(name) - logger.info("%s instrumentation enabled", name) - - -def enable_anthropic(): - name, module = "anthropic", "anthropic" - global _providers - if name in _providers: - logger.debug("%s already enabled", name) - return - - if importlib.util.find_spec(module) is None: - if _strict_mode: - raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") - logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) - return - - from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor - - AnthropicInstrumentor().instrument() - - _providers.add(name) - logger.info("%s instrumentation enabled", name) - - -def enable_google_genai(): - name, module = "google", "google.genai" - - global _providers - if name in _providers: - logger.debug("%s already enabled", name) - return - - if importlib.util.find_spec(module) is None: - module = module.replace(".", "-") - if _strict_mode: - raise ImportError(f"{module} is not installed. Please install it with `pip install {module}`.") - logger.warning("%s is not installed. Please install it with `pip install %s`.", module, module) - return - - from opentelemetry.instrumentation.google_generativeai import GoogleGenerativeAiInstrumentor - - GoogleGenerativeAiInstrumentor().instrument() - - _providers.add(name) - logger.info("%s instrumentation enabled", name) - - -def enable_tracing(providers: list[str]): - if providers == []: - # if no providers are provided, enable all supported providers - providers = ["openai", "anthropic", "google", "agno"] - - logger.info("Enabling tracing for providers: %s", providers) - if "agno" in providers: - enable_agno() - if "openai" in providers: - enable_openai() - if "anthropic" in providers: - enable_anthropic() - if "google" in providers: - enable_google_genai() - - -def _span_processor_on_start(span: trace.Span, parent_context: trace.Context | None = None): - if value := get_value("workflow_name"): - span.set_attribute(SpanAttributes.TRACELOOP_WORKFLOW_NAME, str(value)) - - -def init( - overmind_api_key: str | None = None, - *, - service_name: str | None = None, - environment: str | None = None, - providers: list[str] | None = None, - overmind_base_url: str | None = None, -): - """ - Initialize the Overmind SDK for automatic monitoring. - - Example: - import overmind - overmind.init(service_name="my-backend", environment="production", providers=["openai", "anthropic", "google", "agno"]) - - Args: - overmind_api_key: Your Overmind API key. If not provided, uses OVERMIND_API_KEY env var. - service_name: Name of your service (appears in traces). Defaults to OVERMIND_SERVICE_NAME - env var or "unknown-service". - environment: Environment name (e.g., "production", "staging"). Defaults to - OVERMIND_ENVIRONMENT env var or "development". - providers: List of providers to trace. Supported values: "openai", "anthropic", "google", "agno". - overmind_base_url: Base URL for traces. If not provided, uses OVERMIND_API_URL env var. - """ - global _initialized, _tracer - - if providers is None: - providers = [] - - if _initialized: - # user can call init again with different providers, so we should not skip - # there is no such thing as remove initialization - logger.debug("Overmind SDK already initialized, reinitializing with providers: %s", providers) - enable_tracing(providers) - return - - # Resolve service name and environment - service_name = ( - service_name or os.environ.get("OVERMIND_SERVICE_NAME") or os.environ.get("SERVICE_NAME") or "unknown-service" - ) - environment = ( - environment or os.environ.get("OVERMIND_ENVIRONMENT") or os.environ.get("ENVIRONMENT") or "development" - ) - - overmind_api_key, overmind_base_url = get_api_settings(overmind_api_key, overmind_base_url) - - endpoint = f"{overmind_base_url}/api/v1/traces" - - # Configure OpenTelemetry Provider with rich resource attributes - resource = Resource.create({ - "service.name": service_name, - "service.version": os.environ.get("SERVICE_VERSION", "unknown"), - "deployment.environment": environment, - "overmind.sdk.name": "overmind-python", - "overmind.sdk.version": _SDK_VERSION, - }) - - provider = TracerProvider(resource=resource) - - # Configure OTLP Exporter - headers = {"X-API-Token": overmind_api_key} - - if overmind_base_url: - otlp_exporter = OTLPSpanExporter(endpoint=endpoint, headers=headers) - else: - # No URL provided: fallback to writing spans to disk for local debugging - from overmind_sdk.filexporter import FileSpanExporter - otlp_exporter = FileSpanExporter( - file_path=os.environ.get("OVERMIND_TRACE_FILE", "overmind-traces.jsonl"), - ) - - span_processor = BatchSpanProcessor(otlp_exporter) - provider.add_span_processor(span_processor) - span_processor.on_start = _span_processor_on_start - - # Set global Trace Provider - trace.set_tracer_provider(provider) - - # Store tracer for custom spans - _tracer = trace.get_tracer("overmind", _SDK_VERSION) - enable_tracing(providers) - - _initialized = True - logger.info("Overmind SDK initialized: service=%s, environment=%s", service_name, environment) - - -def get_tracer() -> trace.Tracer: - """ - Get the Overmind tracer for creating custom spans. - - Example: - tracer = overmind.get_tracer() - with tracer.start_as_current_span("my-operation") as span: - span.set_attribute("user.id", user_id) - # ... your code ... - - Returns: - OpenTelemetry Tracer instance. - - Raises: - RuntimeError: If SDK not initialized. - """ - if not _initialized or _tracer is None: - raise RuntimeError("Overmind SDK not initialized. Call overmind.init() first.") - return _tracer - - -def set_user(user_id: str, email: str | None = None, username: str | None = None) -> None: - """ - Associate current trace with a user (like Sentry's set_user). - - Call this in your request handler to tag traces with user info. - - Example: - @app.middleware("http") - async def add_user_context(request: Request, call_next): - if request.state.user: - overmind.set_user(user_id=request.state.user.id) - return await call_next(request) - - Args: - user_id: Unique user identifier. - email: Optional user email. - username: Optional username. - """ - span = trace.get_current_span() - if span.is_recording(): - span.set_attribute("user.id", user_id) - if email: - span.set_attribute("user.email", email) - if username: - span.set_attribute("user.username", username) - - -def set_tag(key: str, value: str) -> None: - """ - Add a custom tag to the current span. - - Example: - overmind.set_tag("feature.flag", "new-checkout-flow") - overmind.set_tag("tenant.id", tenant_id) - - Args: - key: Tag name. - value: Tag value. - """ - span = trace.get_current_span() - if span.is_recording(): - span.set_attribute(key, value) - - -def capture_exception(exception: Exception) -> None: - """ - Record an exception on the current span. - - Example: - try: - risky_operation() - except Exception as e: - overmind.capture_exception(e) - raise - - Args: - exception: The exception to record. - """ - span = trace.get_current_span() - if span.is_recording(): - span.record_exception(exception) - span.set_status(trace.Status(trace.StatusCode.ERROR, str(exception))) diff --git a/overmind_sdk/utils/formatters.py b/overmind_sdk/utils/formatters.py deleted file mode 100644 index e4116c1..0000000 --- a/overmind_sdk/utils/formatters.py +++ /dev/null @@ -1,116 +0,0 @@ -from rich.console import Console, Group -from rich.panel import Panel -from rich.table import Table -from rich.text import Text -from rich.syntax import Syntax -import json - - -res_map = { - "passed": {"color": "green", "emoji": "✅"}, - "altered": {"color": "yellow", "emoji": "⚠️"}, - "rejected": {"color": "red", "emoji": "❌"}, -} - - -def _get_outcome_str(outcome_str: str) -> str: - style = res_map.get(outcome_str, {}).get("color", "white") - emoji = res_map.get(outcome_str, {}).get("emoji", "➡️") - outcome_text = Text(f"{emoji} {outcome_str.upper()}", style=style) - return outcome_text - - -def summarize_proxy_run(result) -> None: - # -- 2. Set up Rich console and formatting maps -- - console = Console() - - # -- 3. Build the Rich renderable components -- - - # A Table for Invocation Results - inv_table = Table(show_header=True, header_style="bold white", title="Results") - inv_table.add_column("Component", style="dim") - inv_table.add_column("Outcome", justify="center") - - overall_input_layer_result = result.input_layer_results.get( - "overall_policy_outcome" - ) - if overall_input_layer_result: - inv_table.add_row("Input Layer", _get_outcome_str(overall_input_layer_result)) - - overall_output_layer_result = result.output_layer_results.get( - "overall_policy_outcome" - ) - if overall_output_layer_result: - inv_table.add_row("Output Layer", _get_outcome_str(overall_output_layer_result)) - - # A Table for Policy Results - pol_table = Table( - show_header=True, header_style="bold white", title="\nPolicy Results" - ) - pol_table.add_column("Layer", style="dim", width=10) - pol_table.add_column("Policy", style="dim", width=20) - pol_table.add_column("Details", width=70) - - for policy, outcome in result.input_layer_results.get("policy_results", {}).items(): - # Assuming the outcome is a dict, format it nicely with Syntax - outcome_str = json.dumps(outcome, indent=2) - outcome_syntax = Syntax( - outcome_str, "json", theme="solarized-dark", word_wrap=True - ) - pol_table.add_row("In", policy, outcome_syntax) - - for policy, outcome in result.output_layer_results.get( - "policy_results", {} - ).items(): - # Assuming the outcome is a dict, format it nicely with Syntax - outcome_str = json.dumps(outcome, indent=2) - outcome_syntax = Syntax( - outcome_str, "json", theme="solarized-dark", word_wrap=True - ) - pol_table.add_row("Out", policy, outcome_syntax) - - # Syntax Panels for Processed Input and Output - input_color = res_map.get(overall_input_layer_result, {}).get("color", "white") - output_color = res_map.get(overall_output_layer_result, {}).get("color", "white") - - input_panel = Panel( - Syntax( - json.dumps(result.processed_input, indent=2)[:2000], - "plain", - theme="solarized-dark", - line_numbers=False, - word_wrap=True, - ), - title="[bold]Processed Input[/bold]", - border_style=input_color, # The border color reflects the outcome! - title_align="left", - ) - - output_panel = Panel( - Syntax( - json.dumps(result.processed_output)[:2000], - "plain", - theme="solarized-dark", - line_numbers=False, - word_wrap=True, - ), - title="[bold]Processed Output[/bold]", - border_style=output_color, # The border color reflects the outcome! - title_align="left", - ) - - # -- 4. Group components and print inside a final Panel -- - final_group = Group( - inv_table, - pol_table, - input_panel, - output_panel, - ) - - console.print( - Panel( - final_group, - title="[bold cyan]Proxy Run Summary[/bold cyan]", - border_style="green", - ) - ) diff --git a/overmind_sdk/utils/serializers.py b/overmind_sdk/utils/serializers.py deleted file mode 100644 index dc9ba9b..0000000 --- a/overmind_sdk/utils/serializers.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - - -def _default_serializer(obj): - if hasattr(obj, "model_dump"): - try: - return obj.model_dump() - except Exception: - pass - if hasattr(obj, "__dict__"): - return str(obj) - return str(obj) - - -def serialize(obj): - return json.dumps(obj, default=_default_serializer) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 29dc06a..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1522 +0,0 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. - -[[package]] -name = "annotated-doc" -version = "0.0.4" -description = "Document parameters, class attributes, return types, and variables inline, with Annotated." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"}, - {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"}, -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.12.1" -description = "High-level concurrency and networking framework on top of asyncio or Trio" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c"}, - {file = "anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] - -[[package]] -name = "asgiref" -version = "3.11.1" -description = "ASGI specs, helper code, and adapters" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133"}, - {file = "asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce"}, -] - -[package.dependencies] -typing_extensions = {version = ">=4", markers = "python_version < \"3.11\""} - -[package.extras] -tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"] - -[[package]] -name = "certifi" -version = "2026.2.25" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, - {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.6" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6"}, - {file = "charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4"}, - {file = "charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb"}, - {file = "charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389"}, - {file = "charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f"}, - {file = "charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4"}, - {file = "charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-win32.whl", hash = "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae"}, - {file = "charset_normalizer-3.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8"}, - {file = "charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8"}, - {file = "charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69"}, - {file = "charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} - -[[package]] -name = "distro" -version = "1.9.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, - {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, - {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "fastapi" -version = "0.135.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e"}, - {file = "fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd"}, -] - -[package.dependencies] -annotated-doc = ">=0.0.2" -pydantic = ">=2.7.0" -starlette = ">=0.46.0" -typing-extensions = ">=4.8.0" -typing-inspection = ">=0.4.2" - -[package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] -standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.73.0" -description = "Common protobufs used in Google APIs" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "googleapis_common_protos-1.73.0-py3-none-any.whl", hash = "sha256:dfdaaa2e860f242046be561e6d6cb5c5f1541ae02cfbcb034371aadb2942b4e8"}, - {file = "googleapis_common_protos-1.73.0.tar.gz", hash = "sha256:778d07cd4fbeff84c6f7c72102f0daf98fa2bfd3fa8bea426edc545588da0b5a"}, -] - -[package.dependencies] -protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" - -[package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0)"] - -[[package]] -name = "h11" -version = "0.16.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, - {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.16" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] - -[[package]] -name = "httpx" -version = "0.28.1" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, - {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" - -[package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "idna" -version = "3.11" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, - {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -perf = ["ipython"] -test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] - -[[package]] -name = "iniconfig" -version = "2.3.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, - {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, -] - -[[package]] -name = "jiter" -version = "0.13.0" -description = "Fast iterable JSON parser." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "jiter-0.13.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e"}, - {file = "jiter-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a638816427006c1e3f0013eb66d391d7a3acda99a7b0cf091eff4497ccea33a"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19928b5d1ce0ff8c1ee1b9bdef3b5bfc19e8304f1b904e436caf30bc15dc6cf5"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:309549b778b949d731a2f0e1594a3f805716be704a73bf3ad9a807eed5eb5721"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcdabaea26cb04e25df3103ce47f97466627999260290349a88c8136ecae0060"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a377af27b236abbf665a69b2bdd680e3b5a0bd2af825cd3b81245279a7606c"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe49d3ff6db74321f144dff9addd4a5874d3105ac5ba7c5b77fac099cfae31ae"}, - {file = "jiter-0.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2113c17c9a67071b0f820733c0893ed1d467b5fcf4414068169e5c2cabddb1e2"}, - {file = "jiter-0.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab1185ca5c8b9491b55ebf6c1e8866b8f68258612899693e24a92c5fdb9455d5"}, - {file = "jiter-0.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9621ca242547edc16400981ca3231e0c91c0c4c1ab8573a596cd9bb3575d5c2b"}, - {file = "jiter-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a7637d92b1c9d7a771e8c56f445c7f84396d48f2e756e5978840ecba2fac0894"}, - {file = "jiter-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c1b609e5cbd2f52bb74fb721515745b407df26d7b800458bd97cb3b972c29e7d"}, - {file = "jiter-0.13.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096"}, - {file = "jiter-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018"}, - {file = "jiter-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411"}, - {file = "jiter-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5"}, - {file = "jiter-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3"}, - {file = "jiter-0.13.0-cp311-cp311-win32.whl", hash = "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1"}, - {file = "jiter-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654"}, - {file = "jiter-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5"}, - {file = "jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663"}, - {file = "jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93"}, - {file = "jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08"}, - {file = "jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2"}, - {file = "jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228"}, - {file = "jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394"}, - {file = "jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92"}, - {file = "jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9"}, - {file = "jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf"}, - {file = "jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663"}, - {file = "jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa"}, - {file = "jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820"}, - {file = "jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68"}, - {file = "jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72"}, - {file = "jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc"}, - {file = "jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b"}, - {file = "jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10"}, - {file = "jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef"}, - {file = "jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6"}, - {file = "jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d"}, - {file = "jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d"}, - {file = "jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0"}, - {file = "jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad"}, - {file = "jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d"}, - {file = "jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df"}, - {file = "jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d"}, - {file = "jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6"}, - {file = "jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f"}, - {file = "jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d"}, - {file = "jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59"}, - {file = "jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe"}, - {file = "jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939"}, - {file = "jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9"}, - {file = "jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6"}, - {file = "jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8"}, - {file = "jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024"}, - {file = "jiter-0.13.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4397ee562b9f69d283e5674445551b47a5e8076fdde75e71bfac5891113dc543"}, - {file = "jiter-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f90023f8f672e13ea1819507d2d21b9d2d1c18920a3b3a5f1541955a85b5504"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed0240dd1536a98c3ab55e929c60dfff7c899fecafcb7d01161b21a99fc8c363"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6207fc61c395b26fffdcf637a0b06b4326f35bfa93c6e92fe1a166a21aeb6731"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00203f47c214156df427b5989de74cb340c65c8180d09be1bf9de81d0abad599"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c26ad6967c9dcedf10c995a21539c3aa57d4abad7001b7a84f621a263a6b605"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a576f5dce9ac7de5d350b8e2f552cf364f32975ed84717c35379a51c7cb198bd"}, - {file = "jiter-0.13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b22945be8425d161f2e536cdae66da300b6b000f1c0ba3ddf237d1bfd45d21b8"}, - {file = "jiter-0.13.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6eeb7db8bc77dc20476bc2f7407a23dbe3d46d9cc664b166e3d474e1c1de4baa"}, - {file = "jiter-0.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:19cd6f85e1dc090277c3ce90a5b7d96f32127681d825e71c9dce28788e39fc0c"}, - {file = "jiter-0.13.0-cp39-cp39-win32.whl", hash = "sha256:dc3ce84cfd4fa9628fe62c4f85d0d597a4627d4242cfafac32a12cc1455d00f7"}, - {file = "jiter-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:9ffda299e417dc83362963966c50cb76d42da673ee140de8a8ac762d4bb2378b"}, - {file = "jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c"}, - {file = "jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2"}, - {file = "jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434"}, - {file = "jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d"}, - {file = "jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a"}, - {file = "jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f"}, - {file = "jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59"}, - {file = "jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19"}, - {file = "jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4"}, -] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, - {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins (>=0.5.0)"] -profiling = ["gprof2dot"] -rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "openai" -version = "2.29.0" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "openai-2.29.0-py3-none-any.whl", hash = "sha256:b7c5de513c3286d17c5e29b92c4c98ceaf0d775244ac8159aeb1bddf840eb42a"}, - {file = "openai-2.29.0.tar.gz", hash = "sha256:32d09eb2f661b38d3edd7d7e1a2943d1633f572596febe64c0cd370c86d52bec"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -jiter = ">=0.10.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.14,<5" - -[package.extras] -aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.9)"] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -realtime = ["websockets (>=13,<16)"] -voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] - -[[package]] -name = "opentelemetry-api" -version = "1.39.1" -description = "OpenTelemetry Python API" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950"}, - {file = "opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c"}, -] - -[package.dependencies] -importlib-metadata = ">=6.0,<8.8.0" -typing-extensions = ">=4.5.0" - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.39.1" -description = "OpenTelemetry Protobuf encoding" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464"}, -] - -[package.dependencies] -opentelemetry-proto = "1.39.1" - -[[package]] -name = "opentelemetry-exporter-otlp-proto-http" -version = "1.39.1" -description = "OpenTelemetry Collector Protobuf over HTTP Exporter" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985"}, - {file = "opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.52,<2.0" -opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.39.1" -opentelemetry-proto = "1.39.1" -opentelemetry-sdk = ">=1.39.1,<1.40.0" -requests = ">=2.7,<3.0" -typing-extensions = ">=4.5.0" - -[package.extras] -gcp-auth = ["opentelemetry-exporter-credential-provider-gcp (>=0.59b0)"] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b1" -description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d"}, - {file = "opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.4,<2.0" -opentelemetry-semantic-conventions = "0.60b1" -packaging = ">=18.0" -wrapt = ">=1.0.0,<2.0.0" - -[[package]] -name = "opentelemetry-instrumentation-asgi" -version = "0.60b1" -description = "ASGI instrumentation for OpenTelemetry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25"}, - {file = "opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f"}, -] - -[package.dependencies] -asgiref = ">=3.0,<4.0" -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.60b1" -opentelemetry-semantic-conventions = "0.60b1" -opentelemetry-util-http = "0.60b1" - -[package.extras] -instruments = ["asgiref (>=3.0,<4.0)"] - -[[package]] -name = "opentelemetry-instrumentation-fastapi" -version = "0.60b1" -description = "OpenTelemetry FastAPI Instrumentation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf"}, - {file = "opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.60b1" -opentelemetry-instrumentation-asgi = "0.60b1" -opentelemetry-semantic-conventions = "0.60b1" -opentelemetry-util-http = "0.60b1" - -[package.extras] -instruments = ["fastapi (>=0.92,<1.0)"] - -[[package]] -name = "opentelemetry-instrumentation-httpx" -version = "0.60b1" -description = "OpenTelemetry HTTPX Instrumentation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"full\"" -files = [ - {file = "opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b"}, - {file = "opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.60b1" -opentelemetry-semantic-conventions = "0.60b1" -opentelemetry-util-http = "0.60b1" -wrapt = ">=1.0.0,<2.0.0" - -[package.extras] -instruments = ["httpx (>=0.18.0)"] - -[[package]] -name = "opentelemetry-instrumentation-logging" -version = "0.60b1" -description = "OpenTelemetry Logging instrumentation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"full\"" -files = [ - {file = "opentelemetry_instrumentation_logging-0.60b1-py3-none-any.whl", hash = "sha256:f2e18cbc7e1dd3628c80e30d243897fdc93c5b7e0c8ae60abd2b9b6a99f82343"}, - {file = "opentelemetry_instrumentation_logging-0.60b1.tar.gz", hash = "sha256:98f4b9c7aeb9314a30feee7c002c7ea9abea07c90df5f97fb058b850bc45b89a"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.60b1" - -[[package]] -name = "opentelemetry-instrumentation-requests" -version = "0.60b1" -description = "OpenTelemetry requests instrumentation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"full\"" -files = [ - {file = "opentelemetry_instrumentation_requests-0.60b1-py3-none-any.whl", hash = "sha256:eec9fac3fab84737f663a2e08b12cb095b4bd67643b24587a8ecfa3cf4d0ca4c"}, - {file = "opentelemetry_instrumentation_requests-0.60b1.tar.gz", hash = "sha256:9a1063c16c44a3ba6e81870c4fa42a0fac3ecef5a4d60a11d0976eec9046f3d4"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.60b1" -opentelemetry-semantic-conventions = "0.60b1" -opentelemetry-util-http = "0.60b1" - -[package.extras] -instruments = ["requests (>=2.0,<3.0)"] - -[[package]] -name = "opentelemetry-overmind" -version = "0.53.0" -description = "" -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "opentelemetry_overmind-0.53.0-py3-none-any.whl", hash = "sha256:cabed0dbc960cfaa7c929456447e622cd435769430e580498914c016e8eed6bf"}, - {file = "opentelemetry_overmind-0.53.0.tar.gz", hash = "sha256:f6a1b8d15ba2dcf5478c7499e1414bac56ba9ae2f2a48ce39ffcd64b38896f0c"}, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -description = "OpenTelemetry Python Proto" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007"}, - {file = "opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8"}, -] - -[package.dependencies] -protobuf = ">=5.0,<7.0" - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -description = "OpenTelemetry Python SDK" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c"}, - {file = "opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6"}, -] - -[package.dependencies] -opentelemetry-api = "1.39.1" -opentelemetry-semantic-conventions = "0.60b1" -typing-extensions = ">=4.5.0" - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -description = "OpenTelemetry Semantic Conventions" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb"}, - {file = "opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953"}, -] - -[package.dependencies] -opentelemetry-api = "1.39.1" -typing-extensions = ">=4.5.0" - -[[package]] -name = "opentelemetry-semantic-conventions-ai" -version = "0.4.16" -description = "OpenTelemetry Semantic Conventions Extension for Large Language Models" -optional = false -python-versions = "<4,>=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_semantic_conventions_ai-0.4.16-py3-none-any.whl", hash = "sha256:d5ddd0df387b969da82e3e0a8b7415e91d2fc7ce13de7efc2690a7939932b2e0"}, - {file = "opentelemetry_semantic_conventions_ai-0.4.16.tar.gz", hash = "sha256:572eb878d8b81e50f1e53d2a5c1b441e7d34918ee01c846ff62485204d660c22"}, -] - -[package.dependencies] -opentelemetry-sdk = ">=1.38.0,<2" -opentelemetry-semantic-conventions = ">=0.59b0" - -[[package]] -name = "opentelemetry-util-http" -version = "0.60b1" -description = "Web util for OpenTelemetry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199"}, - {file = "opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6"}, -] - -[[package]] -name = "overmind-instrumentation-agno" -version = "0.53.0" -description = "OpenTelemetry Agno instrumentation" -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "overmind_instrumentation_agno-0.53.0-py3-none-any.whl", hash = "sha256:59651877d41817cd25c25a226bdaf2ca76fa649cb8e6170da87be60801022970"}, - {file = "overmind_instrumentation_agno-0.53.0.tar.gz", hash = "sha256:64941651bc1f7da9607c16c22669830b8c098353c6b29eaefea0d6e248dc823b"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.28.0,<2" -opentelemetry-instrumentation = ">=0.59b0" -opentelemetry-semantic-conventions = ">=0.59b0" -opentelemetry-semantic-conventions-ai = ">=0.4.13,<0.5.0" - -[package.extras] -instruments = ["agno"] - -[[package]] -name = "overmind-instrumentation-anthropic" -version = "0.53.0" -description = "OpenTelemetry Anthropic instrumentation" -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "overmind_instrumentation_anthropic-0.53.0-py3-none-any.whl", hash = "sha256:f963a201a9c73ddc037bc9125741cc768794bb20b0550b2de5153e76baad2002"}, - {file = "overmind_instrumentation_anthropic-0.53.0.tar.gz", hash = "sha256:28c0b59dbf0ff7596567e56542b13bdb9b0b59c765814541f8e874125a339d70"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.38.0,<2" -opentelemetry-instrumentation = ">=0.59b0" -opentelemetry-semantic-conventions = ">=0.59b0" -opentelemetry-semantic-conventions-ai = ">=0.4.14,<0.5.0" - -[package.extras] -instruments = ["anthropic"] - -[[package]] -name = "overmind-instrumentation-google-generativeai" -version = "0.53.0" -description = "OpenTelemetry Google Generative AI instrumentation" -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "overmind_instrumentation_google_generativeai-0.53.0-py3-none-any.whl", hash = "sha256:28b6d4348ffb7e07516b38afa2830479172e85b60d9c7954c680b058d89202a6"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.38.0,<2" -opentelemetry-instrumentation = ">=0.59b0" -opentelemetry-semantic-conventions = ">=0.59b0" -opentelemetry-semantic-conventions-ai = ">=0.4.13,<0.5.0" - -[package.extras] -instruments = ["google-genai"] - -[[package]] -name = "overmind-instrumentation-openai" -version = "0.53.1" -description = "OpenTelemetry OpenAI instrumentation" -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "overmind_instrumentation_openai-0.53.1-py3-none-any.whl", hash = "sha256:4f603d061987434bf35e721705678f6c63996d8d1cc49950b84e5068a4f29af7"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.38.0,<2" -opentelemetry-instrumentation = ">=0.59b0" -opentelemetry-overmind = ">=0.53.0" -opentelemetry-semantic-conventions = ">=0.59b0" -opentelemetry-semantic-conventions-ai = ">=0.4.13,<0.5.0" - -[package.extras] -instruments = ["openai"] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] - -[[package]] -name = "protobuf" -version = "6.33.6" -description = "" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3"}, - {file = "protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326"}, - {file = "protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593"}, - {file = "protobuf-6.33.6-cp39-cp39-win32.whl", hash = "sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e"}, - {file = "protobuf-6.33.6-cp39-cp39-win_amd64.whl", hash = "sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf"}, - {file = "protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901"}, - {file = "protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135"}, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, - {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.41.5" -typing-extensions = ">=4.14.1" -typing-inspection = ">=0.4.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, - {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, -] - -[package.dependencies] -typing-extensions = ">=4.14.1" - -[[package]] -name = "pygments" -version = "2.20.0" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, - {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pytest" -version = "9.0.3" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, - {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, -] - -[package.dependencies] -colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} -iniconfig = ">=1.0.1" -packaging = ">=22" -pluggy = ">=1.5,<2" -pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "requests" -version = "2.33.1" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a"}, - {file = "requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517"}, -] - -[package.dependencies] -certifi = ">=2023.5.7" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.26,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] - -[[package]] -name = "rich" -version = "14.3.3" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d"}, - {file = "rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "ruff" -version = "0.4.10" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, - {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, - {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, - {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, - {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, - {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "starlette" -version = "1.0.0" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b"}, - {file = "starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149"}, -] - -[package.dependencies] -anyio = ">=3.6.2,<5" -typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} - -[package.extras] -full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] - -[[package]] -name = "tomli" -version = "2.4.0" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version == \"3.10\"" -files = [ - {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, - {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, - {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, - {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, - {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, - {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, - {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, - {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, - {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, - {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, - {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, - {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, - {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, - {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, - {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, - {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, - {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, - {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, - {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, -] - -[[package]] -name = "tqdm" -version = "4.67.3" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf"}, - {file = "tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] -discord = ["requests"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, - {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[[package]] -name = "urllib3" -version = "2.6.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, -] - -[package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "wrapt" -version = "1.17.3" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, - {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, - {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, - {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, - {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, - {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, - {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, - {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, - {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, - {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, - {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, - {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, - {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, - {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, - {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, - {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, - {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, - {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, - {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, - {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, - {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, - {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, - {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, - {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, -] - -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - -[extras] -full = ["opentelemetry-instrumentation-httpx", "opentelemetry-instrumentation-logging", "opentelemetry-instrumentation-requests"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.10,<4" -content-hash = "395f16d0f8f5b7d4135420d66da9b0d25e8d06ec654e8be6cd73cb1e518b5bac" diff --git a/pyproject.toml b/pyproject.toml index fb46493..7c537e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [tool.poetry] -name = "overmind-sdk" -version = "0.1.38" +name = "overmind" +version = "0.1.39" description = "Python client for Overmind API" authors = ["Overmind Ltd"] readme = "README.md" -packages = [{include = "overmind_sdk"}] +packages = [{include = "overmind"}] homepage = "https://github.com/overmind-core/overmind-python" repository = "https://github.com/overmind-core/overmind-python" keywords = ["overmind", "ai", "api", "client", "policy", "enforcement"] @@ -65,7 +65,7 @@ openai = "^2.26.0" line-length = 120 indent-width = 4 target-version = "py310" -include = ["overmind_sdk/**/*.py", "tests/**/*.py"] +include = ["overmind/**/*.py", "tests/**/*.py"] preview = true [tool.ruff.format] diff --git a/scripts/example_tracer.py b/scripts/example_tracer.py index ca8cd42..14f008c 100644 --- a/scripts/example_tracer.py +++ b/scripts/example_tracer.py @@ -1,7 +1,7 @@ import time import random import openai -from overmind_sdk import init, tool, workflow, entry_point +from overmind import init, tool, workflow, entry_point init(service_name="example-tracer", environment="development") diff --git a/tests/conftest.py b/tests/conftest.py index b013574..57f1ea2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,20 +2,9 @@ Shared pytest fixtures and configuration. """ -import pytest from unittest.mock import Mock -from overmind_sdk import OvermindClient - - -@pytest.fixture -def overmind(): - """Create a test OvermindClient instance.""" - return OvermindClient( - overmind_api_key="test_token", - base_url="http://test.com", - openai_api_key="test_openai_key", - ) +import pytest @pytest.fixture @@ -26,20 +15,3 @@ def mock_response(): response.content = b'{"status": "success"}' response.json.return_value = {"status": "success"} return response - - -@pytest.fixture -def mock_policy_response(): - """Create a mock response for policy operations.""" - response = Mock() - response.status_code = 200 - response.json.return_value = { - "policy_id": "test_policy", - "policy_description": "Test policy", - "parameters": {"param1": "value1"}, - "engine": "test_engine", - "is_input_policy": True, - "is_output_policy": False, - } - response.content = b'{"policy_id": "test_policy"}' - return response diff --git a/tests/test_client.py b/tests/test_client.py deleted file mode 100644 index 2cd8d0f..0000000 --- a/tests/test_client.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -Integration tests for the Overmind Python client. -""" - -from unittest.mock import Mock, patch - -import pytest - -from overmind_sdk import OvermindClient -from overmind_sdk.exceptions import ( - OvermindAPIError, - OvermindAuthenticationError, - OvermindError, -) -from overmind_sdk.models import AgentCreateRequest, PolicyCreateRequest - - -class TestClientIntegration: - """Integration tests to verify all client components work together.""" - - def setup_method(self): - """Set up test fixtures.""" - self.client = OvermindClient( - overmind_api_key="test_token", - base_url="http://test.com", - openai_api_key="test_openai_key", - ) - - def test_client_has_all_subclients(self): - """Test that the client has all expected subclients.""" - assert hasattr(self.client, "policies") - - def test_client_has_dynamic_provider_access(self): - """Test that the client supports dynamic provider access.""" - assert hasattr(self.client, "openai") - assert self.client.openai is not None - - @patch("requests.Session.request") - def test_end_to_end_workflow(self, mock_request): - """Test a complete workflow using all client components.""" - # Mock responses for different operations - mock_request.side_effect = [ - # Create policy response - Mock( - status_code=200, - json=lambda: { - "policy_id": "test_policy", - "policy_description": "Test policy", - "parameters": {"param1": "value1"}, - "policy_template": "test_template", - "stats": {}, - "is_input_policy": True, - "is_output_policy": False, - "created_at": "2023-01-01T00:00:00Z", - "updated_at": "2023-01-01T00:00:00Z", - }, - content=b'{"policy_id": "test_policy"}', - ), - # Invoke response - Mock( - status_code=200, - json=lambda: { - "llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}, - "input_layer_results": {}, - "output_layer_results": {}, - "processed_input": "test input", - "processed_output": "test output", - "span_context": {}, - }, - content=b'{"llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}}', - ), - ] - - # Create a policy - policy = self.client.policies.create( - policy_id="test_policy", - policy_template="test_template", - policy_description="Test policy", - parameters={"param1": "value1"}, - is_input_policy=True, - is_output_policy=False, - ) - # Create methods return success response, not the created object - assert policy is not None - - # Make an invocation - result = self.client.invoke( - client_path="openai.chat.completions.create", - client_call_params={ - "model": "gpt-5", - "messages": [{"role": "user", "content": "Hello"}], - }, - ) - assert result.llm_client_response["choices"][0]["message"]["content"] == "Hello" - assert result.processed_input == "test input" - - # Verify all expected requests were made - assert mock_request.call_count == 2 - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_client_main.py b/tests/test_client_main.py deleted file mode 100644 index 49ec5d1..0000000 --- a/tests/test_client_main.py +++ /dev/null @@ -1,192 +0,0 @@ -""" -Tests for the main OvermindClient class. -""" - -import os -from unittest.mock import Mock, patch - -import pytest - -from overmind_sdk import OvermindClient -from overmind_sdk.exceptions import ( - OvermindAPIError, - OvermindAuthenticationError, -) - - -class TestOvermindClient: - """Test cases for the OvermindClient class.""" - - def setup_method(self): - """Set up test fixtures.""" - self.client = OvermindClient( - overmind_api_key="test_token", - base_url="http://test.com", - openai_api_key="test_openai_key", - ) - - def test_client_initialization(self): - """Test client initialization.""" - assert self.client.overmind_api_key == "test_token" - assert self.client.base_url == "http://test.com" - assert self.client.provider_parameters == {"openai_api_key": "test_openai_key"} - assert "X-API-Token" in self.client.session.headers - assert "Content-Type" in self.client.session.headers - - # Test sub-clients are initialized - assert hasattr(self.client, "policies") - - def test_dynamic_provider_access(self): - """Test dynamic provider access.""" - # Test that we can access a provider - openai_proxy = self.client.openai - assert openai_proxy is not None - assert openai_proxy.path_parts == ["openai"] - - def test_provider_method_chaining(self): - """Test provider method chaining.""" - # Test that we can chain method calls - proxy = self.client.openai.chat.completions - assert proxy.path_parts == ["openai", "chat", "completions"] - - @patch("requests.Session.request") - def test_provider_invocation(self, mock_request): - """Test provider invocation through dynamic access.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}, - "input_layer_results": {}, - "output_layer_results": {}, - "processed_input": "test input", - "processed_output": "test output", - "span_context": {}, - } - mock_response.content = b'{"llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}}' - mock_request.return_value = mock_response - - # Call the provider through dynamic access - result = self.client.openai.chat.completions.create( - model="gpt-4o", messages=[{"role": "user", "content": "Hello"}] - ) - - # Verify the request was made with correct parameters - call_args = mock_request.call_args - request_data = call_args[1]["json"] - # client_call_params is serialized to JSON string - import json - - client_call_params = json.loads(request_data["client_call_params"]) - assert client_call_params["model"] == "gpt-4o" - assert request_data["client_init_params"]["openai_api_key"] == "test_openai_key" - - @patch("requests.Session.request") - def test_successful_api_request(self, mock_request): - """Test successful API request.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {"status": "success"} - mock_response.content = b'{"status": "success"}' - mock_request.return_value = mock_response - - result = self.client._make_request("GET", "test/endpoint") - assert result == {"status": "success"} - - @patch("requests.Session.request") - def test_authentication_error(self, mock_request): - """Test authentication error handling.""" - mock_response = Mock() - mock_response.status_code = 401 - mock_response.content = b'{"detail": "Invalid token"}' - mock_request.return_value = mock_response - - with pytest.raises(OvermindAuthenticationError): - self.client._make_request("GET", "test/endpoint") - - @patch("requests.Session.request") - def test_api_error(self, mock_request): - """Test API error handling.""" - mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"detail": "Bad request"} - mock_response.content = b'{"detail": "Bad request"}' - mock_request.return_value = mock_response - - with pytest.raises(OvermindAPIError) as exc_info: - self.client._make_request("GET", "test/endpoint") - - assert exc_info.value.status_code == 400 - assert "Bad request" in str(exc_info.value) - - @patch("requests.Session.request") - def test_invoke_method(self, mock_request): - """Test invoke method.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}, - "input_layer_results": {}, - "output_layer_results": {}, - "processed_input": "test input", - "processed_output": "test output", - "span_context": {}, - } - mock_response.content = b'{"llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}}' - mock_request.return_value = mock_response - - result = self.client.invoke( - client_path="openai.chat.completions.create", - client_call_params={ - "model": "gpt-4o", - "messages": [{"role": "user", "content": "Hello"}], - }, - agent_id="test_agent", - ) - - assert result.llm_client_response["choices"][0]["message"]["content"] == "Hello" - assert result.processed_input == "test input" - - @patch("requests.Session.request") - def test_invoke_method_with_custom_init_params(self, mock_request): - """Test invoke method with custom client_init_params.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}, - "input_layer_results": {}, - "output_layer_results": {}, - "processed_input": "test input", - "processed_output": "test output", - "span_context": {}, - } - mock_response.content = b'{"llm_client_response": {"choices": [{"message": {"content": "Hello"}}]}}' - mock_request.return_value = mock_response - - custom_init_params = {"api_key": "custom_key"} - result = self.client.invoke( - client_path="openai.chat.completions.create", - client_call_params={ - "model": "gpt-5-mini", - "messages": [{"role": "user", "content": "Hello"}], - }, - agent_id="test_agent", - client_init_params=custom_init_params, - ) - - # Verify the request was made with custom init params - call_args = mock_request.call_args - request_data = call_args[1]["json"] - assert request_data["client_init_params"] == custom_init_params - - @patch.dict(os.environ, {"OVERMIND_API_KEY": "env_test_token"}) - def test_client_initialization_with_env_var(self): - """Test client initialization using environment variable.""" - client = OvermindClient( - base_url="http://test.com", - openai_api_key="test_openai_key", - ) - assert client.overmind_api_key == "env_test_token" - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_dump_logs.py b/tests/test_dump_logs.py index c5392c2..6d610d5 100644 --- a/tests/test_dump_logs.py +++ b/tests/test_dump_logs.py @@ -2,14 +2,14 @@ import os import unittest from unittest.mock import MagicMock -from overmind_sdk.utils.dump_logs import ingest_logs +from overmind.utils.dump_logs import ingest_logs current_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") -@unittest.mock.patch("overmind_sdk.utils.dump_logs.get_tracer") -@unittest.mock.patch("overmind_sdk.utils.dump_logs.init") +@unittest.mock.patch("overmind.utils.dump_logs.get_tracer") +@unittest.mock.patch("overmind.utils.dump_logs.init") @pytest.mark.parametrize("filepath", ["logs.jsonl", "logs.json", "logs.csv"]) def test_dump_logs(init_mock: MagicMock, mock_get_tracer: MagicMock, filepath): init_mock.return_value = None @@ -18,8 +18,8 @@ def test_dump_logs(init_mock: MagicMock, mock_get_tracer: MagicMock, filepath): mock_get_tracer.assert_called() -@unittest.mock.patch("overmind_sdk.utils.dump_logs.get_tracer") -@unittest.mock.patch("overmind_sdk.utils.dump_logs.init") +@unittest.mock.patch("overmind.utils.dump_logs.get_tracer") +@unittest.mock.patch("overmind.utils.dump_logs.init") @pytest.mark.parametrize("filepath", ["logs_mapped.jsonl", "logs_mapped.json", "logs_mapped.csv"]) def test_dump_logs_with_mapping(init_mock: MagicMock, mock_get_tracer: MagicMock, filepath): init_mock.return_value = None diff --git a/tests/test_models.py b/tests/test_models.py index 31e9e3e..b927915 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -4,7 +4,7 @@ import pytest -from overmind_sdk.models import AgentCreateRequest, PolicyCreateRequest +from overmind.models import AgentCreateRequest class TestModels: @@ -22,67 +22,6 @@ def test_agent_create_request(self): assert agent.agent_id == "test_agent" assert agent.agent_model == "gpt-5-mini" assert agent.agent_description == "Test agent" - assert agent.input_policies == [] - assert agent.output_policies == [] - - def test_agent_create_request_with_policies(self): - """Test AgentCreateRequest model with policies.""" - agent_data = { - "agent_id": "test_agent", - "agent_model": "gpt-5-mini", - "agent_description": "Test agent", - "input_policies": ["policy1", "policy2"], - "output_policies": ["policy3"], - } - - agent = AgentCreateRequest(**agent_data) - assert agent.agent_id == "test_agent" - assert agent.input_policies == ["policy1", "policy2"] - assert agent.output_policies == ["policy3"] - - def test_policy_create_request(self): - """Test PolicyCreateRequest model.""" - policy_data = { - "policy_id": "test_policy", - "policy_description": "Test policy", - "parameters": {"param1": "value1"}, - "policy_template": "test_template", - "is_input_policy": True, - "is_output_policy": False, - } - - policy = PolicyCreateRequest(**policy_data) - assert policy.policy_id == "test_policy" - assert policy.policy_description == "Test policy" - assert policy.parameters == {"param1": "value1"} - assert policy.policy_template == "test_template" - assert policy.is_input_policy is True - assert policy.is_output_policy is False - - def test_policy_create_request_validation(self): - """Test PolicyCreateRequest validation.""" - # Test that at least one of is_input_policy or is_output_policy must be True - with pytest.raises(ValueError): - PolicyCreateRequest( - policy_id="test_policy", - policy_description="Test policy", - parameters={}, - policy_template="test_template", - is_input_policy=False, - is_output_policy=False, - ) - - # Test that both can be True - policy = PolicyCreateRequest( - policy_id="test_policy", - policy_description="Test policy", - parameters={}, - policy_template="test_template", - is_input_policy=True, - is_output_policy=True, - ) - assert policy.is_input_policy is True - assert policy.is_output_policy is True if __name__ == "__main__": diff --git a/tests/test_policies.py b/tests/test_policies.py deleted file mode 100644 index 5122469..0000000 --- a/tests/test_policies.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Tests for the PoliciesClient subclient. -""" - -from unittest.mock import Mock, patch - -import pytest - -from overmind_sdk import OvermindClient - - -class TestPoliciesClient: - """Test cases for the PoliciesClient sub-client.""" - - def setup_method(self): - """Set up test fixtures.""" - self.client = OvermindClient( - overmind_api_key="test_token", - base_url="http://test.com", - openai_api_key="test_openai_key", - ) - - @patch("requests.Session.request") - def test_create_policy(self, mock_request): - """Test creating a policy.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "policy_id": "test_policy", - "policy_description": "Test policy", - "parameters": {"param1": "value1"}, - "policy_template": "test_template", - "stats": {}, - "is_input_policy": True, - "is_output_policy": False, - "created_at": "2023-01-01T00:00:00Z", - "updated_at": "2023-01-01T00:00:00Z", - } - mock_response.content = b'{"policy_id": "test_policy"}' - mock_request.return_value = mock_response - - # Create policy using the correct API signature - result = self.client.policies.create( - policy_id="test_policy", - policy_template="test_template", - policy_description="Test policy", - parameters={"param1": "value1"}, - is_input_policy=True, - is_output_policy=False, - ) - # Create methods return success response, not the created object - assert result is not None - - @patch("requests.Session.request") - def test_list_policies(self, mock_request): - """Test listing policies.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = [ - { - "policy_id": "policy1", - "policy_description": "Policy 1", - "parameters": {}, - "policy_template": "test_template", - "stats": {}, - "is_input_policy": True, - "is_output_policy": False, - "created_at": "2023-01-01T00:00:00Z", - "updated_at": "2023-01-01T00:00:00Z", - }, - { - "policy_id": "policy2", - "policy_description": "Policy 2", - "parameters": {}, - "policy_template": "test_template", - "stats": {}, - "is_input_policy": False, - "is_output_policy": True, - "created_at": "2023-01-01T00:00:00Z", - "updated_at": "2023-01-01T00:00:00Z", - }, - ] - mock_response.content = b'[{"policy_id": "policy1"}]' - mock_request.return_value = mock_response - - result = self.client.policies.list() - assert len(result) == 2 - assert result[0].policy_id == "policy1" - assert result[1].policy_id == "policy2" - - @patch("requests.Session.request") - def test_get_policy(self, mock_request): - """Test getting a specific policy.""" - mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "policy_id": "test_policy", - "policy_description": "Test policy", - "parameters": {"param1": "value1"}, - "policy_template": "test_template", - "stats": {}, - "is_input_policy": True, - "is_output_policy": False, - "created_at": "2023-01-01T00:00:00Z", - "updated_at": "2023-01-01T00:00:00Z", - } - mock_response.content = b'{"policy_id": "test_policy"}' - mock_request.return_value = mock_response - - result = self.client.policies.get("test_policy") - assert result.policy_id == "test_policy" - assert result.policy_description == "Test policy" - - @patch("requests.Session.request") - def test_delete_policy(self, mock_request): - """Test deleting a policy.""" - mock_response = Mock() - mock_response.status_code = 204 - mock_response.content = b"" - mock_request.return_value = mock_response - - # Should not raise an exception - self.client.policies.delete("test_policy") - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_sdk.py b/tests/test_sdk.py index debb6ae..cd4fc8a 100644 --- a/tests/test_sdk.py +++ b/tests/test_sdk.py @@ -8,7 +8,7 @@ @pytest.fixture(autouse=True) def reset_sdk_state(): """Reset SDK state before each test.""" - import overmind_sdk.tracing as sdk + import overmind.tracing as sdk sdk._initialized = False sdk._tracer = None @@ -24,10 +24,10 @@ def mock_opentelemetry(): mock_openai_class = MagicMock() with ( - patch("overmind_sdk.tracing.TracerProvider") as mock_provider, - patch("overmind_sdk.tracing.OTLPSpanExporter") as mock_exporter, - patch("overmind_sdk.tracing.BatchSpanProcessor") as mock_processor, - patch("overmind_sdk.tracing.trace") as mock_trace, + patch("overmind.tracing.TracerProvider") as mock_provider, + patch("overmind.tracing.OTLPSpanExporter") as mock_exporter, + patch("overmind.tracing.BatchSpanProcessor") as mock_processor, + patch("overmind.tracing.trace") as mock_trace, ): # Set up tracer mock mock_tracer = MagicMock() @@ -44,7 +44,7 @@ def mock_opentelemetry(): def test_sdk_init_configures_tracing(mock_opentelemetry): """Test that calling init configures OpenTelemetry correctly.""" - from overmind_sdk import tracing + from overmind import tracing with ( patch.object(tracing, "FastAPIInstrumentor", create=True) as mock_fastapi, @@ -76,13 +76,13 @@ def test_sdk_init_configures_tracing(mock_opentelemetry): def test_sdk_init_only_once(): """Test that init only runs once.""" - from overmind_sdk import tracing + from overmind import tracing with ( - patch("overmind_sdk.tracing.TracerProvider"), - patch("overmind_sdk.tracing.OTLPSpanExporter"), - patch("overmind_sdk.tracing.BatchSpanProcessor"), - patch("overmind_sdk.tracing.trace") as mock_trace, + patch("overmind.tracing.TracerProvider"), + patch("overmind.tracing.OTLPSpanExporter"), + patch("overmind.tracing.BatchSpanProcessor"), + patch("overmind.tracing.trace") as mock_trace, ): tracing.init(overmind_api_key="test_key", overmind_base_url="http://localhost:4318") tracing.init(overmind_api_key="test_key", overmind_base_url="http://localhost:4318") @@ -93,13 +93,13 @@ def test_sdk_init_only_once(): def test_sdk_init_handles_missing_deps(): """Test that init doesn't crash if optional instrumentation libraries are missing.""" - from overmind_sdk import tracing + from overmind import tracing with ( - patch("overmind_sdk.tracing.TracerProvider"), - patch("overmind_sdk.tracing.OTLPSpanExporter"), - patch("overmind_sdk.tracing.BatchSpanProcessor"), - patch("overmind_sdk.tracing.trace"), + patch("overmind.tracing.TracerProvider"), + patch("overmind.tracing.OTLPSpanExporter"), + patch("overmind.tracing.BatchSpanProcessor"), + patch("overmind.tracing.trace"), ): # Should not raise exception even if instrumentors fail to import tracing.init(overmind_api_key="test_key", overmind_base_url="http://localhost:4318") @@ -107,7 +107,7 @@ def test_sdk_init_handles_missing_deps(): def test_get_tracer_before_init(): """Test that get_tracer raises if SDK not initialized.""" - from overmind_sdk import tracing + from overmind import tracing with pytest.raises(RuntimeError, match="not initialized"): tracing.get_tracer() @@ -115,7 +115,7 @@ def test_get_tracer_before_init(): def test_get_tracer_after_init(mock_opentelemetry): """Test that get_tracer returns tracer after init.""" - from overmind_sdk import tracing + from overmind import tracing tracing.init(overmind_api_key="test_key", overmind_base_url="http://localhost:4318") @@ -125,7 +125,7 @@ def test_get_tracer_after_init(mock_opentelemetry): def test_set_user(mock_opentelemetry): """Test that set_user adds user attributes to current span.""" - from overmind_sdk import tracing + from overmind import tracing mock_span = MagicMock() mock_span.is_recording.return_value = True @@ -140,7 +140,7 @@ def test_set_user(mock_opentelemetry): def test_set_tag(mock_opentelemetry): """Test that set_tag adds custom attributes to current span.""" - from overmind_sdk import tracing + from overmind import tracing mock_span = MagicMock() mock_span.is_recording.return_value = True @@ -154,7 +154,7 @@ def test_set_tag(mock_opentelemetry): def test_capture_exception(mock_opentelemetry): """Test that capture_exception records exception on current span.""" - from overmind_sdk import tracing + from overmind import tracing mock_span = MagicMock() mock_span.is_recording.return_value = True @@ -187,14 +187,14 @@ def ping(): def test_service_name_from_env(): """Test that service name can be set via environment variable.""" - from overmind_sdk import tracing + from overmind import tracing with ( - patch("overmind_sdk.tracing.TracerProvider") as mock_provider, - patch("overmind_sdk.tracing.OTLPSpanExporter"), - patch("overmind_sdk.tracing.BatchSpanProcessor"), - patch("overmind_sdk.tracing.trace"), - patch("overmind_sdk.tracing.Resource") as mock_resource, + patch("overmind.tracing.TracerProvider") as mock_provider, + patch("overmind.tracing.OTLPSpanExporter"), + patch("overmind.tracing.BatchSpanProcessor"), + patch("overmind.tracing.trace"), + patch("overmind.tracing.Resource") as mock_resource, patch.dict(os.environ, {"OVERMIND_SERVICE_NAME": "env-service"}), ): tracing.init(overmind_api_key="test_key", overmind_base_url="http://localhost:4318") diff --git a/tests/test_spans.py b/tests/test_spans.py index 4998788..5b64109 100644 --- a/tests/test_spans.py +++ b/tests/test_spans.py @@ -4,7 +4,7 @@ import requests from time import sleep from opentelemetry import trace as otel_trace -from overmind_sdk.tracing import init +from overmind.tracing import init from openai import OpenAI diff --git a/tests/test_tracer.py b/tests/test_tracer.py index 7d0ea83..6736b7e 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -5,12 +5,12 @@ import pytest from unittest.mock import MagicMock, patch from opentelemetry.trace import StatusCode -from overmind_sdk.tracer import observe +from overmind.tracing import observe @pytest.fixture(autouse=True) def reset_sdk_state(): """Reset SDK state before each test.""" - import overmind_sdk.tracing as tracing + import overmind.tracing as tracing tracing._initialized = False tracing._tracer = None @@ -37,7 +37,7 @@ def test_observe_sync_basic(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def add_numbers(a: int, b: int): @@ -58,7 +58,7 @@ def test_observe_with_custom_span_name(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe(span_name="custom_operation") def my_function(x: int): @@ -76,7 +76,7 @@ def test_observe_captures_inputs(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def process_data(name: str, age: int, metadata: dict): @@ -95,7 +95,7 @@ def test_observe_captures_outputs(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def get_result(): @@ -115,7 +115,7 @@ def test_observe_handles_exceptions(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def failing_function(): @@ -140,7 +140,7 @@ def test_observe_async(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe(span_name="async_operation") async def async_add(a: int, b: int): @@ -162,7 +162,7 @@ def test_observe_async_with_exception(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() async def async_fail(): @@ -183,7 +183,7 @@ def test_observe_with_kwargs(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def greet(name: str, greeting: str = "Hello"): @@ -201,7 +201,7 @@ def test_observe_preserves_function_metadata(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def documented_function(param: int) -> int: @@ -218,7 +218,7 @@ def test_observe_with_complex_types(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def process_complex(data: dict, items: list): @@ -236,7 +236,7 @@ def test_observe_with_no_args(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def get_constant(): @@ -254,7 +254,7 @@ def test_observe_with_positional_only_args(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): @observe() def multiply(x, y): @@ -272,7 +272,7 @@ def test_observe_skips_self_in_class_method(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): class TestClass: def __init__(self): @@ -300,7 +300,7 @@ def test_observe_skips_cls_in_classmethod(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): class TestClass: class_value = 20 @@ -328,7 +328,7 @@ def test_observe_async_class_method(mock_tracer): mock_tracer_obj, mock_span = mock_tracer - with patch("overmind_sdk.tracer.get_tracer", return_value=mock_tracer_obj): + with patch("overmind.tracing.get_tracer", return_value=mock_tracer_obj): class AsyncTestClass: def __init__(self): diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..bda0207 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3 @@ +version = 1 +revision = 3 +requires-python = ">=3.13"