"""Configuration for the third-party API proxy with billing integration.""" from __future__ import annotations from pydantic import BaseModel, Field class SubmitRouteConfig(BaseModel): """Identifies a submit request — triggers billing reserve + task state tracking.""" method: str = Field(default="POST", description="HTTP method to match (case-insensitive)") path_pattern: str = Field( description="Glob-style path pattern. Use ** to match any sub-path, e.g. /openapi/v2/**" ) exclude_path_pattern: str | None = Field( default=None, description="If set, paths matching this pattern are excluded from submit handling", ) task_id_jsonpath: str = Field( description="Dot-path into the *response* body to extract the provider task ID, e.g. taskId" ) frozen_amount: float | None = Field( default=None, ge=0, description="Optional route-level override for billing reserve payload frozenAmount", ) frozen_type: int | None = Field( default=None, description="Optional route-level override for billing reserve payload frozenType", ) frozen_token: int | None = Field( default=None, ge=0, description="Optional route-level override for billing reserve payload estimatedInputTokens/estimatedOutputTokens when frozenType=1", ) class QueryRouteConfig(BaseModel): """Identifies a query/poll request — checks for terminal status + triggers billing finalize.""" method: str = Field(default="POST", description="HTTP method to match (case-insensitive)") path_pattern: str = Field(description="Glob-style path pattern for the query endpoint") request_task_id_jsonpath: str = Field( description="Dot-path into the *request* body to extract the task ID being queried" ) status_jsonpath: str = Field( description="Dot-path into the response body to read the task status value" ) success_values: list[str] = Field( default_factory=list, description="Status string values that indicate successful terminal state, e.g. [\"SUCCESS\"]", ) failure_values: list[str] = Field( default_factory=list, description="Status string values that indicate failed terminal state, e.g. [\"FAILED\", \"CANCELLED\"]", ) usage_jsonpath: str | None = Field( default=None, description=( "Dot-path into the response body for the actual monetary cost to pass to billing finalize. " "E.g. usage.thirdPartyConsumeMoney" ), ) usage_jsonpaths: list[str] = Field( default_factory=list, description=( "Optional list of dot-paths into the response body to extract monetary costs and sum them. " "When set, values from all valid paths are added together. " "Example: [\"usage.thirdPartyConsumeMoney\", \"usage.consumeMoney\"]" ), ) class ThirdPartyProviderConfig(BaseModel): """Configuration for a single third-party API platform.""" base_url: str = Field(description="Base URL of the provider, e.g. https://www.runninghub.cn") api_key_env: str | None = Field( default=None, description="Name of the environment variable holding the API key", ) timeout_seconds: float = Field( default=30.0, gt=0, description="HTTP request timeout when forwarding to the provider", ) frozen_amount: float = Field( default=0.0, ge=0, description="Amount to reserve in billing reserve payload (frozenAmount)", ) frozen_type: int | None = Field( default=None, description="Billing frozen type for this provider (frozenType). If omitted, falls back to billing.frozen_type", ) frozen_token: int = Field( default=0, ge=0, description="Estimated token amount used for reserve payload when frozenType=1", ) submit_routes: list[SubmitRouteConfig] = Field( default_factory=list, description="Route patterns that identify submit (task-create) requests", ) query_routes: list[QueryRouteConfig] = Field( default_factory=list, description="Route patterns that identify query/poll requests", ) class ThirdPartyProxyConfig(BaseModel): """Top-level configuration for the third-party API proxy.""" enabled: bool = Field(default=False, description="Enable the proxy endpoint") providers: dict[str, ThirdPartyProviderConfig] = Field( default_factory=dict, description="Keyed by provider name (used in the URL path /api/proxy/{provider}/...)", )