Files
PolyGateway/src/polygateway/__init__.py
T
iomgaa 9474c76ab0 feat: add an optional per-call wall-clock deadline
Give one logical call an optional hard wall-clock boundary (issue #22).
Leaving it unset keeps 1.3.5 behaviour verbatim: the timeout context is
never entered when deadline_s is None.

- new deadline.py: ensure_call_deadline() range check (None or a finite
  positive number; bool/0/nan/inf and out-of-range ints are rejected as
  ValueError so OverflowError never leaks) plus with_call_deadline(),
  which distinguishes an expiry from a TimeoutError raised by the body
  or its cleanup via a local-variable identity comparison rather than
  cm.expired() alone
- new CallDeadlineExceeded: deliberately outside the four categories and
  not a GatewayUnavailableError, and carries no retry_after_s
- new {SCOPE}__CALL_DEADLINE_S key, guarded on the env, direct
  construction and dataclasses.replace paths
- three clients take a call_deadline_s constructor argument and a
  keyword-only per-call override on chat/embed/recognize_text/
  parse_layout; None inherits the assembled value
- validation runs before the awaitable is created, so an illegal value
  cannot strand an un-awaited coroutine
- one embed call shares a single deadline across all of its batches
- import-linter gains a polygateway.deadline layer

- cover where the deadline lands: backoff sleep, admission polling,
  the structured re-ask ladder and embedding's batch loop, plus the
  empty-texts early return that stays outside it
- cover what an expiry costs: exactly one terminal_failure row carrying
  error_type=CallDeadlineExceeded, a cancelled attempt row sharing its
  logical_call_id, cleanup that outlives the deadline (lower bound only)
  and an already-billed success being discarded
- pin the injected clock as orthogonal: a 10^6 second jump never expires
  a call, yet total_latency_ms still reads that clock
2026-09-10 04:14:44 -04:00

102 lines
2.6 KiB
Python

"""PolyGateway: 实验室统一的大语言模型(LLM/VLM/OCR)调度与中转库。
治理单位是一次模型调用: 多源选择、限流、错误分类与重试、熔断、响应缓存、
流式活性看门狗、遥测。架构单一事实源见 research-wiki/ARCHITECTURE.md。
顶层导出即公共 API 面: 错误四分类必须从这里引用(ARCH §5.1 约定②)。
"""
from polygateway.client import GatewayClient, gather_bounded
from polygateway.config import EmbeddingSettings, GatewaySettings, OcrSettings
from polygateway.embedding import EmbeddingClient
from polygateway.errors import (
AllSourcesExhausted,
CallDeadlineExceeded,
CircuitOpenError,
GatewayUnavailableError,
GovernanceBackendError,
PolyGatewayError,
RequestRejectedError,
ResultInvalidError,
SourceDeadError,
SourceNotConfiguredError,
TransientError,
)
from polygateway.ocr import OcrClient
from polygateway.pricing import ModelPrice, PricingTable
from polygateway.providers import (
DEFAULT_PROFILES,
ProviderProfile,
ThinkingWire,
register_provider,
)
from polygateway.telemetry.schema import telemetry_schema_sql
from polygateway.thinking import (
ThinkingCapability,
ThinkingResolution,
ThinkingUnsupportedError,
get_capability,
register_capability,
resolve_thinking,
)
from polygateway.types import (
EFFORT_ORDER,
CallStats,
Effort,
EmbeddingResponse,
LLMResponse,
OcrLayoutElement,
OcrLayoutResult,
OcrTextResult,
SourceConfig,
TelemetryStatus,
ThinkingObservation,
)
__version__ = "1.3.5"
__all__ = [
"DEFAULT_PROFILES",
"EFFORT_ORDER",
"Effort",
"AllSourcesExhausted",
"CallStats",
"CallDeadlineExceeded",
"CircuitOpenError",
"EmbeddingClient",
"EmbeddingResponse",
"EmbeddingSettings",
"GatewayClient",
"GatewaySettings",
"GatewayUnavailableError",
"GovernanceBackendError",
"LLMResponse",
"ModelPrice",
"OcrClient",
"OcrLayoutElement",
"OcrLayoutResult",
"OcrSettings",
"OcrTextResult",
"PolyGatewayError",
"PricingTable",
"ProviderProfile",
"RequestRejectedError",
"ResultInvalidError",
"SourceConfig",
"SourceDeadError",
"SourceNotConfiguredError",
"TelemetryStatus",
"ThinkingCapability",
"ThinkingObservation",
"ThinkingResolution",
"ThinkingUnsupportedError",
"ThinkingWire",
"TransientError",
"__version__",
"gather_bounded",
"get_capability",
"register_capability",
"register_provider",
"resolve_thinking",
"telemetry_schema_sql",
]