From 8eadbf04496d278cfa33a38323faf023a8401f7b Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 11:35:16 +0800 Subject: [PATCH 1/6] feat(server): add Scalar API reference --- pyproject.toml | 1 + src/powercontext/server/app.py | 23 +++++++++++++++++++++++ src/powercontext/server/middleware.py | 2 +- tests/test_server.py | 14 ++++++++++++++ uv.lock | 13 ++++++++++++- 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 89cd74e0e..9c4a3138b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,6 +65,7 @@ server = [ "powercontext[builtin]", "prometheus-client>=0.21,<1", "pydantic-settings>=2.7,<3", + "scalar-fastapi>=1.8.2,<2", "uvicorn>=0.34,<1", ] tracing-otlp = [ diff --git a/src/powercontext/server/app.py b/src/powercontext/server/app.py index 8cfd96edd..efd0640da 100644 --- a/src/powercontext/server/app.py +++ b/src/powercontext/server/app.py @@ -32,6 +32,7 @@ from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from opentelemetry.trace import SpanKind +from scalar_fastapi import AgentScalarConfig, get_scalar_api_reference from starlette.middleware import Middleware from starlette.middleware.base import RequestResponseEndpoint from starlette.types import Lifespan @@ -390,6 +391,7 @@ from powercontext.server.metrics import ServerMetrics from powercontext.server.tracing import ServerTracing +_SCALAR_JS_URL = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.66.1" REQUEST_ID_HEADER = "X-PowerContext-Request-ID" REPORT_SELECTION_DIGEST_HEADER = "X-PowerContext-Selection-Digest" REPORT_DIGEST_HEADER = "X-PowerContext-Report-Digest" @@ -686,6 +688,12 @@ async def unexpected_error(request: Request, error: Exception) -> JSONResponse: _add_route(app, APPROVE_ARTIFACT_CANDIDATE, approve_artifact_candidate) _add_route(app, REJECT_ARTIFACT_CANDIDATE, reject_artifact_candidate) _add_route(app, REVISE_ARTIFACT_CANDIDATE, revise_artifact_candidate) + app.add_api_route( + "/scalar", + scalar_api_reference, + include_in_schema=False, + methods=["GET"], + ) def canonical_openapi() -> dict[str, Any]: if app.openapi_schema is None: @@ -701,6 +709,21 @@ def canonical_openapi() -> dict[str, Any]: return app +async def scalar_api_reference(request: Request) -> Response: + """Render the runtime OpenAPI contract with Scalar.""" + + return get_scalar_api_reference( + content=request.app.openapi(), + title=f"{API_TITLE} Reference", + scalar_js_url=_SCALAR_JS_URL, + scalar_favicon_url="data:,", + with_default_fonts=False, + show_developer_tools="never", + telemetry=False, + agent=AgentScalarConfig(disabled=True), + ) + + async def get_liveness() -> HealthResponse: return HealthResponse(status="ok") diff --git a/src/powercontext/server/middleware.py b/src/powercontext/server/middleware.py index abd9891cf..ef71608d4 100644 --- a/src/powercontext/server/middleware.py +++ b/src/powercontext/server/middleware.py @@ -25,7 +25,7 @@ from powercontext.http import ErrorDetail, ErrorResponse from powercontext.server.context import is_internal_bridge -_PUBLIC_PATHS = frozenset({"/", "/handoff-reports", "/reviews", "/skills", "/health/live", "/health/ready"}) +_PUBLIC_PATHS = frozenset({"/", "/handoff-reports", "/reviews", "/scalar", "/skills", "/health/live", "/health/ready"}) _PUBLIC_PATH_PREFIXES = ("/static/",) diff --git a/tests/test_server.py b/tests/test_server.py index 7cb939bea..1c35e6d75 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -269,6 +269,18 @@ def test_liveness_adds_a_server_owned_request_id() -> None: assert "X-Request-ID" not in response.headers +def test_scalar_reference_embeds_the_canonical_openapi_contract() -> None: + response = TestClient(create_app()).get("/scalar") + + assert response.status_code == 200 + assert response.headers["content-type"].startswith("text/html") + assert "PowerContext API Reference" in response.text + assert '"openapi": "3.0.3"' in response.text + assert '"/v1/context/prepare"' in response.text + assert "@scalar/api-reference@1.66.1" in response.text + assert "proxyUrl" not in response.text + + def test_server_factory_optionally_requires_bearer_authentication() -> None: app = create_server_app( settings=ServerSettings( @@ -284,6 +296,7 @@ def test_server_factory_optionally_requires_bearer_authentication() -> None: protected_metrics = client.get("/metrics") accepted_metrics = client.get("/metrics", headers={"Authorization": "Bearer server-secret"}) liveness = client.get("/health/live") + scalar_reference = client.get("/scalar") assert missing.status_code == 401 assert missing.headers["WWW-Authenticate"] == "Bearer" @@ -300,6 +313,7 @@ def test_server_factory_optionally_requires_bearer_authentication() -> None: assert protected_metrics.status_code == 401 assert accepted_metrics.status_code == 200 assert liveness.status_code == 200 + assert scalar_reference.status_code == 200 def test_readiness_reports_unavailable_bindings() -> None: diff --git a/uv.lock b/uv.lock index ab5b5a3c3..e7ba90b3d 100644 --- a/uv.lock +++ b/uv.lock @@ -987,7 +987,7 @@ name = "importlib-metadata" version = "9.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } wheels = [ @@ -2108,6 +2108,7 @@ server = [ { name = "pydantic-ai-slim", extra = ["anthropic", "openai"] }, { name = "pydantic-settings" }, { name = "pyobvector" }, + { name = "scalar-fastapi" }, { name = "sqlalchemy", extra = ["asyncio"] }, { name = "sqlite-vec" }, { name = "uvicorn" }, @@ -2172,6 +2173,7 @@ requires-dist = [ { name = "pyobvector", marker = "extra == 'seekdb'", specifier = ">=0.2.28,<0.3" }, { name = "pyobvector", marker = "extra == 'server'", specifier = ">=0.2.28,<0.3" }, { name = "rfc8785", specifier = ">=0.1.4,<1" }, + { name = "scalar-fastapi", marker = "extra == 'server'", specifier = ">=1.8.2,<2" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'builtin'", specifier = ">=2,<3" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'seekdb'", specifier = ">=2,<3" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'server'", specifier = ">=2,<3" }, @@ -3137,6 +3139,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" }, ] +[[package]] +name = "scalar-fastapi" +version = "1.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/81/c0c70776a3d7d371ee06d38d26a8d361c97439d46f79acb6d67cf6c760ad/scalar_fastapi-1.8.2.tar.gz", hash = "sha256:0de09b8c63f78c1052792faa200d740b2ccaeeb88ac54e7ea633ac4edc6fde82", size = 8371, upload-time = "2026-04-09T22:41:24.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/b1/b9a482620479b2801a4dfa5c4c10b4cc280097e894114e0298f21a0fca55/scalar_fastapi-1.8.2-py3-none-any.whl", hash = "sha256:d96e2c8b3676491eaebb4ec8d9f4de77adb2374d86f87d321546fa6f084e8cb8", size = 7677, upload-time = "2026-04-09T22:41:23.209Z" }, +] + [[package]] name = "secretstorage" version = "3.5.0" From baf9fba45329f71f8277199698e8b0bcd9593ba8 Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 11:35:25 +0800 Subject: [PATCH 2/6] docs: publish Scalar HTTP API reference --- .github/workflows/deploy-docs.yml | 2 +- .github/workflows/release.yml | 2 +- Makefile | 12 ++++++--- docs/api/index.html | 39 ++++++++++++++++++++++++++++ docs/en/docs/reference/interfaces.md | 10 ++++--- docs/zh/docs/reference/interfaces.md | 12 +++++---- zensical.toml | 6 +++-- 7 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 docs/api/index.html diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 261624757..c5803a38d 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -27,7 +27,7 @@ jobs: uses: ./.github/actions/setup-python-env - name: Build documentation - run: uv run zensical build --clean + run: make docs-build - name: Upload artifact uses: actions/upload-pages-artifact@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47f40afbe..fb9e64fee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -122,7 +122,7 @@ jobs: uses: ./.github/actions/setup-python-env - name: Build documentation - run: uv run zensical build --clean + run: make docs-build - name: Upload artifact uses: actions/upload-pages-artifact@v5 diff --git a/Makefile b/Makefile index 6d44eb16f..051535e66 100644 --- a/Makefile +++ b/Makefile @@ -140,12 +140,18 @@ publish: ## Publish a release to PyPI. .PHONY: build-and-publish build-and-publish: build publish ## Build and publish. +.PHONY: docs-build +docs-build: ## Build the documentation and publish the canonical OpenAPI contract. + @uv run zensical build --clean -s + @install -D -m 0644 openapi/powercontext.yaml site/api/openapi.yaml + .PHONY: docs-test -docs-test: ## Test if documentation can be built without warnings or errors - @uv run zensical build -s +docs-test: docs-build ## Test if documentation can be built without warnings or errors + @test -f site/api/index.html + @cmp --silent openapi/powercontext.yaml site/api/openapi.yaml .PHONY: docs -docs: ## Build and serve the documentation +docs: docs-build ## Build and serve the documentation @uv run zensical serve .PHONY: help diff --git a/docs/api/index.html b/docs/api/index.html new file mode 100644 index 000000000..2208ec609 --- /dev/null +++ b/docs/api/index.html @@ -0,0 +1,39 @@ + + + + + PowerContext HTTP API Reference + + + + +
+ + + + + diff --git a/docs/en/docs/reference/interfaces.md b/docs/en/docs/reference/interfaces.md index 59987d9de..c133aef89 100644 --- a/docs/en/docs/reference/interfaces.md +++ b/docs/en/docs/reference/interfaces.md @@ -285,10 +285,12 @@ want the supplied SQLite or OceanBase-backed implementation in the same process. ## HTTP and MCP -The Server publishes its OpenAPI document at `/openapi.json`, readiness at `/health/ready`, capabilities at -`/v1/capabilities`, and Streamable HTTP MCP at `/mcp` by default. HTTP is the complete application contract. MCP is a -curated agent-facing projection of Memory and Candidate Review operations. The five Candidate Review operations use the -same validation, `expected_version` concurrency checks, and approval transaction over HTTP and MCP. +The Server publishes a Scalar API reference at `/scalar`, its OpenAPI document at `/openapi.json`, readiness at +`/health/ready`, capabilities at `/v1/capabilities`, and Streamable HTTP MCP at `/mcp` by default. The Scalar reference +remains public when bearer authentication is enabled, but the operations it describes retain their normal authentication +requirements. HTTP is the complete application contract. MCP is a curated agent-facing projection of Memory and +Candidate Review operations. The five Candidate Review operations use the same validation, `expected_version` +concurrency checks, and approval transaction over HTTP and MCP. Readiness is `ready` with HTTP 200 when all checks pass, `degraded` with HTTP 200 when only configured inference checks fail, and `not_ready` with HTTP 503 when the Runtime or database fails. Dependency checks use `ready`, `unavailable`, `timeout`, or `misconfigured`; an intentionally unbound Runtime reports `not_ready` for the `runtime` check. diff --git a/docs/zh/docs/reference/interfaces.md b/docs/zh/docs/reference/interfaces.md index a1ca194bf..8d0eab1ad 100644 --- a/docs/zh/docs/reference/interfaces.md +++ b/docs/zh/docs/reference/interfaces.md @@ -263,11 +263,13 @@ Discovery 不进入 Review。显式调用 `import_external_skill` 并提供精 ## HTTP 和 MCP -Server 在 `/openapi.json` 提供 OpenAPI 文档,在 `/health/ready` 提供就绪检查,在 `/v1/capabilities` -提供能力信息,并默认在 `/mcp` 提供 Streamable HTTP MCP。HTTP 是完整应用契约,MCP 是面向 Agent 的 -Memory 与 Candidate Review operation 子集。五个 Candidate Review operation 通过 HTTP 和 MCP 使用相同的 -validation、`expected_version` 并发校验和 approval transaction。Experience/Skill generation、exact read、 -external Registry operation 和低阶 proposal operation 仍只通过 HTTP 提供。 +Server 在 `/scalar` 提供 Scalar API reference,在 `/openapi.json` 提供 OpenAPI 文档,在 `/health/ready` +提供就绪检查,在 `/v1/capabilities` 提供能力信息,并默认在 `/mcp` 提供 Streamable HTTP MCP。启用 Bearer +authentication 后,Scalar reference 仍可公开访问,但其中描述的 operation 继续遵守各自的认证要求。HTTP 是 +完整应用契约,MCP 是面向 Agent 的 Memory 与 Candidate Review operation 子集。五个 Candidate Review operation +通过 HTTP 和 MCP 使用相同的 validation、`expected_version` 并发校验和 approval transaction。 +Experience/Skill generation、exact read、external Registry operation 和低阶 proposal operation 仍只通过 HTTP +提供。 所有检查通过时 readiness 为 HTTP 200 的 `ready`;只有已配置的推理检查失败时为 HTTP 200 的 `degraded`; Runtime 或数据库失败时为 HTTP 503 的 `not_ready`。依赖检查使用 `ready`、`unavailable`、`timeout` 或 `misconfigured`;有意不绑定 Runtime 时,`runtime` 检查使用 `not_ready`。 diff --git a/zensical.toml b/zensical.toml index ae3526af4..d54634c19 100644 --- a/zensical.toml +++ b/zensical.toml @@ -40,6 +40,7 @@ nav = [ { "Reference" = [ { "Interfaces" = "en/docs/reference/interfaces.md" }, { "Configuration" = "en/docs/reference/configuration.md" }, + { "HTTP API Reference" = "api/index.html" }, ] }, ] }, { "Development" = [ @@ -48,7 +49,7 @@ nav = [ { "Pydantic AI Inference" = "en/development/pydantic-ai-inference.md" }, { "Remote Access Implementation" = "en/development/remote-access-implementation.md" }, { "Server Web UI" = "en/development/server-web-ui.md" }, - { "API Reference" = "en/modules.md" }, + { "Python API Reference" = "en/modules.md" }, ] }, { "Blog" = [ { "Overview" = "en/blog/index.md" }, @@ -115,6 +116,7 @@ nav = [ { "参考" = [ { "接口" = "zh/docs/reference/interfaces.md" }, { "配置" = "zh/docs/reference/configuration.md" }, + { "HTTP API 参考" = "api/index.html" }, ] }, ] }, { "开发" = [ @@ -123,7 +125,7 @@ nav = [ { "Pydantic AI 推理" = "zh/development/pydantic-ai-inference.md" }, { "远程访问实现" = "zh/development/remote-access-implementation.md" }, { "Server Web UI" = "zh/development/server-web-ui.md" }, - { "API 参考" = "zh/modules.md" }, + { "Python API 参考" = "zh/modules.md" }, ] }, { "博客" = [ { "概览" = "zh/blog/index.md" }, From ce2ed692612fdac4225ad4a9955366b800c2f7c6 Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 11:42:40 +0800 Subject: [PATCH 3/6] fix(server): serve Scalar reference at /docs --- docs/en/docs/reference/interfaces.md | 2 +- docs/zh/docs/reference/interfaces.md | 2 +- src/powercontext/server/app.py | 4 +++- src/powercontext/server/middleware.py | 2 +- tests/test_server.py | 6 ++++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/en/docs/reference/interfaces.md b/docs/en/docs/reference/interfaces.md index c133aef89..f30fc5562 100644 --- a/docs/en/docs/reference/interfaces.md +++ b/docs/en/docs/reference/interfaces.md @@ -285,7 +285,7 @@ want the supplied SQLite or OceanBase-backed implementation in the same process. ## HTTP and MCP -The Server publishes a Scalar API reference at `/scalar`, its OpenAPI document at `/openapi.json`, readiness at +The Server publishes a Scalar API reference at `/docs`, its OpenAPI document at `/openapi.json`, readiness at `/health/ready`, capabilities at `/v1/capabilities`, and Streamable HTTP MCP at `/mcp` by default. The Scalar reference remains public when bearer authentication is enabled, but the operations it describes retain their normal authentication requirements. HTTP is the complete application contract. MCP is a curated agent-facing projection of Memory and diff --git a/docs/zh/docs/reference/interfaces.md b/docs/zh/docs/reference/interfaces.md index 8d0eab1ad..793b9e1d1 100644 --- a/docs/zh/docs/reference/interfaces.md +++ b/docs/zh/docs/reference/interfaces.md @@ -263,7 +263,7 @@ Discovery 不进入 Review。显式调用 `import_external_skill` 并提供精 ## HTTP 和 MCP -Server 在 `/scalar` 提供 Scalar API reference,在 `/openapi.json` 提供 OpenAPI 文档,在 `/health/ready` +Server 在 `/docs` 提供 Scalar API reference,在 `/openapi.json` 提供 OpenAPI 文档,在 `/health/ready` 提供就绪检查,在 `/v1/capabilities` 提供能力信息,并默认在 `/mcp` 提供 Streamable HTTP MCP。启用 Bearer authentication 后,Scalar reference 仍可公开访问,但其中描述的 operation 继续遵守各自的认证要求。HTTP 是 完整应用契约,MCP 是面向 Agent 的 Memory 与 Candidate Review operation 子集。五个 Candidate Review operation diff --git a/src/powercontext/server/app.py b/src/powercontext/server/app.py index efd0640da..ebe042b4f 100644 --- a/src/powercontext/server/app.py +++ b/src/powercontext/server/app.py @@ -573,6 +573,8 @@ def create_app( title=API_TITLE, version=API_VERSION, description=API_DESCRIPTION, + docs_url=None, + redoc_url=None, lifespan=lifespan, middleware=list(middleware), ) @@ -689,7 +691,7 @@ async def unexpected_error(request: Request, error: Exception) -> JSONResponse: _add_route(app, REJECT_ARTIFACT_CANDIDATE, reject_artifact_candidate) _add_route(app, REVISE_ARTIFACT_CANDIDATE, revise_artifact_candidate) app.add_api_route( - "/scalar", + "/docs", scalar_api_reference, include_in_schema=False, methods=["GET"], diff --git a/src/powercontext/server/middleware.py b/src/powercontext/server/middleware.py index ef71608d4..605140f04 100644 --- a/src/powercontext/server/middleware.py +++ b/src/powercontext/server/middleware.py @@ -25,7 +25,7 @@ from powercontext.http import ErrorDetail, ErrorResponse from powercontext.server.context import is_internal_bridge -_PUBLIC_PATHS = frozenset({"/", "/handoff-reports", "/reviews", "/scalar", "/skills", "/health/live", "/health/ready"}) +_PUBLIC_PATHS = frozenset({"/", "/docs", "/handoff-reports", "/reviews", "/skills", "/health/live", "/health/ready"}) _PUBLIC_PATH_PREFIXES = ("/static/",) diff --git a/tests/test_server.py b/tests/test_server.py index 1c35e6d75..0cb1484aa 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -270,7 +270,8 @@ def test_liveness_adds_a_server_owned_request_id() -> None: def test_scalar_reference_embeds_the_canonical_openapi_contract() -> None: - response = TestClient(create_app()).get("/scalar") + client = TestClient(create_app()) + response = client.get("/docs") assert response.status_code == 200 assert response.headers["content-type"].startswith("text/html") @@ -279,6 +280,7 @@ def test_scalar_reference_embeds_the_canonical_openapi_contract() -> None: assert '"/v1/context/prepare"' in response.text assert "@scalar/api-reference@1.66.1" in response.text assert "proxyUrl" not in response.text + assert client.get("/scalar").status_code == 404 def test_server_factory_optionally_requires_bearer_authentication() -> None: @@ -296,7 +298,7 @@ def test_server_factory_optionally_requires_bearer_authentication() -> None: protected_metrics = client.get("/metrics") accepted_metrics = client.get("/metrics", headers={"Authorization": "Bearer server-secret"}) liveness = client.get("/health/live") - scalar_reference = client.get("/scalar") + scalar_reference = client.get("/docs") assert missing.status_code == 401 assert missing.headers["WWW-Authenticate"] == "Bearer" From ea433b3e62337dc078053e3b7c0d29bb9b55e8aa Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 11:56:30 +0800 Subject: [PATCH 4/6] fix(docs): stage OpenAPI contract for preview --- .gitignore | 1 + Makefile | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 78c9b0eac..08bf2099a 100644 --- a/.gitignore +++ b/.gitignore @@ -175,6 +175,7 @@ venv.bak/ # mkdocs/zensical documentation /site +/docs/api/openapi.yaml e2e/bub/results/ # mypy diff --git a/Makefile b/Makefile index 051535e66..949b33571 100644 --- a/Makefile +++ b/Makefile @@ -142,8 +142,8 @@ build-and-publish: build publish ## Build and publish. .PHONY: docs-build docs-build: ## Build the documentation and publish the canonical OpenAPI contract. - @uv run zensical build --clean -s - @install -D -m 0644 openapi/powercontext.yaml site/api/openapi.yaml + @install -D -m 0644 openapi/powercontext.yaml docs/api/openapi.yaml + @trap 'rm -f docs/api/openapi.yaml' EXIT; uv run zensical build --clean -s .PHONY: docs-test docs-test: docs-build ## Test if documentation can be built without warnings or errors @@ -151,8 +151,9 @@ docs-test: docs-build ## Test if documentation can be built without warnings or @cmp --silent openapi/powercontext.yaml site/api/openapi.yaml .PHONY: docs -docs: docs-build ## Build and serve the documentation - @uv run zensical serve +docs: ## Build and serve the documentation + @install -D -m 0644 openapi/powercontext.yaml docs/api/openapi.yaml + @trap 'rm -f docs/api/openapi.yaml' EXIT; uv run zensical serve $(ARGS) .PHONY: help help: From f2384cac5cfd9471cb12288d843dd663c6dbf313 Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 14:03:11 +0800 Subject: [PATCH 5/6] fix(bub): remove core version floor --- e2e/bub/uv.lock | 3 ++- integrations/bub/pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/bub/uv.lock b/e2e/bub/uv.lock index 022834992..fb56d27cf 100644 --- a/e2e/bub/uv.lock +++ b/e2e/bub/uv.lock @@ -1648,6 +1648,7 @@ requires-dist = [ { name = "pyobvector", marker = "extra == 'seekdb'", specifier = ">=0.2.28,<0.3" }, { name = "pyobvector", marker = "extra == 'server'", specifier = ">=0.2.28,<0.3" }, { name = "rfc8785", specifier = ">=0.1.4,<1" }, + { name = "scalar-fastapi", marker = "extra == 'server'", specifier = ">=1.8.2,<2" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'builtin'", specifier = ">=2,<3" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'seekdb'", specifier = ">=2,<3" }, { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'server'", specifier = ">=2,<3" }, @@ -1696,7 +1697,7 @@ dependencies = [ requires-dist = [ { name = "bub", specifier = ">=0.4.0,<0.5.0" }, { name = "httpx", specifier = ">=0.28,<1" }, - { name = "powercontext", extras = ["client"], specifier = ">=0.0.3" }, + { name = "powercontext", extras = ["client"] }, { name = "pydantic-settings", specifier = ">=2.7,<3" }, ] diff --git a/integrations/bub/pyproject.toml b/integrations/bub/pyproject.toml index 4a3f36128..8063d100f 100644 --- a/integrations/bub/pyproject.toml +++ b/integrations/bub/pyproject.toml @@ -20,7 +20,7 @@ requires-python = ">=3.12,<4.0" dependencies = [ "bub>=0.4.0,<0.5.0", "httpx>=0.28,<1", - "powercontext[client]>=0.0.3", + "powercontext[client]", "pydantic-settings>=2.7,<3", ] From 51836c8691bbd4825ed60b8d01239e609baaacae Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Thu, 27 Aug 2026 14:06:09 +0800 Subject: [PATCH 6/6] test(packaging): drop dependency string assertion --- tests/pydantic_ai_adapter/test_packaging.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/pydantic_ai_adapter/test_packaging.py b/tests/pydantic_ai_adapter/test_packaging.py index 750dc27cd..ffe578f58 100644 --- a/tests/pydantic_ai_adapter/test_packaging.py +++ b/tests/pydantic_ai_adapter/test_packaging.py @@ -20,8 +20,6 @@ import re import subprocess import sys -import zipfile -from email.parser import Parser from pathlib import Path from shutil import which @@ -67,21 +65,6 @@ def built_wheels(tmp_path_factory: pytest.TempPathFactory) -> dict[str, Path]: return {name: _build_wheel(project, root / name) for name, project in _PROJECTS.items()} -def _requires_dist(wheel: Path) -> list[str]: - with zipfile.ZipFile(wheel) as archive: - metadata_name = next(name for name in archive.namelist() if name.endswith(".dist-info/METADATA")) - metadata = Parser().parsestr(archive.read(metadata_name).decode("utf-8")) - return metadata.get_all("Requires-Dist", []) - - -@pytest.mark.parametrize("package", ["pydantic-ai", "bub"]) -def test_integration_wheels_require_the_first_core_release_with_shared_capture( - built_wheels: dict[str, Path], - package: str, -) -> None: - assert "powercontext[client]>=0.0.3" in _requires_dist(built_wheels[package]) - - def test_openai_install_command_is_consistent_across_public_guides() -> None: for path in (_PYDANTIC_AI_README, *_PYDANTIC_AI_HOW_TOS): assert _OPENAI_INSTALL in path.read_text(encoding="utf-8")