9474c76ab0
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
89 lines
3.0 KiB
TOML
89 lines
3.0 KiB
TOML
[build-system]
|
||
requires = ["setuptools>=68.0"]
|
||
build-backend = "setuptools.build_meta"
|
||
|
||
[project]
|
||
name = "polygateway"
|
||
version = "1.3.5"
|
||
description = "PolyGateway:实验室统一的大语言模型(LLM/VLM/OCR)调度与中转库——多源、限流、重试、熔断、缓存、遥测"
|
||
# registry 包页面的正文只认这一项:缺了页面就是一片空白(1.1.2 的教训,twine 会警告
|
||
# long_description missing 但不阻塞上传)。README 在打包时被固化进产物,发布后再改无效。
|
||
readme = "README.md"
|
||
requires-python = ">=3.12"
|
||
dependencies = [
|
||
"httpx>=0.27",
|
||
"pydantic>=2.8",
|
||
"pydantic-settings>=2.4",
|
||
# 显式声明(2026-07-20 人类确认): config.py 的多源动态键族直接使用 dotenv_values,
|
||
# 不能只依赖 pydantic-settings 的传递安装
|
||
"python-dotenv>=1.0",
|
||
"loguru>=0.7",
|
||
]
|
||
|
||
[project.optional-dependencies]
|
||
redis = ["redis>=5.0"]
|
||
postgres = ["asyncpg>=0.29"]
|
||
structured = ["json-repair>=0.28"]
|
||
sdk = ["openai>=1.30"]
|
||
dev = [
|
||
"pytest>=8.0",
|
||
"pytest-cov>=5.0",
|
||
"pytest-asyncio>=0.23",
|
||
"ruff>=0.6",
|
||
"radon>=6.0",
|
||
"import-linter>=2.0",
|
||
]
|
||
|
||
[project.urls]
|
||
Homepage = "https://gitea.iomgaa.online/iomgaa/PolyGateway"
|
||
Changelog = "https://gitea.iomgaa.online/iomgaa/PolyGateway/src/branch/main/CHANGELOG.md"
|
||
Issues = "https://gitea.iomgaa.online/iomgaa/PolyGateway/issues"
|
||
|
||
[tool.setuptools.packages.find]
|
||
where = ["src"]
|
||
|
||
[tool.pytest.ini_options]
|
||
pythonpath = ["src"]
|
||
testpaths = ["tests"]
|
||
asyncio_mode = "auto"
|
||
# slow(真实等待的时间语义变体,单文件 12-15 分钟)默认排除;
|
||
# 显式 `pytest -m slow` 覆盖(CLI 的 -m 后到优先)。M2 T12 全量跑。
|
||
addopts = "-m 'not slow'"
|
||
markers = [
|
||
"requires_redis: 需要可达的 Redis(无则 skip)",
|
||
"requires_llm: 需要真实 LLM 网关(无则 skip,输出落 tests/outputs/)",
|
||
"slow: 慢速测试(CI 按需跑)",
|
||
]
|
||
|
||
[tool.ruff]
|
||
target-version = "py312"
|
||
line-length = 100
|
||
|
||
[tool.ruff.lint]
|
||
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "SIM", "TCH"]
|
||
ignore = ["E501"]
|
||
|
||
[tool.ruff.lint.isort]
|
||
known-first-party = ["polygateway"]
|
||
|
||
# 依赖纪律(ARCHITECTURE.md §8):ports/types/errors/streaming 为最内层;
|
||
# middleware 只依赖端口;transports/backends/telemetry/structured/providers/sources
|
||
# 只实现端口且互不依赖;client 是唯一组装层。
|
||
# 注:M1 模块落地前 lint-imports 由 Makefile 门控跳过。
|
||
[tool.importlinter]
|
||
root_packages = ["polygateway"]
|
||
|
||
[[tool.importlinter.contracts]]
|
||
name = "洋葱分层:client → config → middleware → 实现层(互不依赖)→ 叶子 → 内核"
|
||
type = "layers"
|
||
layers = [
|
||
"polygateway.client",
|
||
"polygateway.config",
|
||
"polygateway.middleware",
|
||
"polygateway.transports | polygateway.backends | polygateway.telemetry | polygateway.structured",
|
||
"polygateway.thinking",
|
||
"polygateway.deadline",
|
||
"polygateway.providers : polygateway.sources",
|
||
"polygateway.ports : polygateway.types : polygateway.errors : polygateway.streaming",
|
||
]
|