diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93b9907..39b0acf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,42 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project uses **CalVer `YY.M.PP`** (PEP 440 may normalise patch numbers
for the Python wheel — e.g. `26.06.00` → `26.6.0`).
+## [26.6.10] - 2026-06-15
+
+### Changed
+
+- **Adopted pyfly's separate management port (app `8080`, management `9090`).**
+ Upgraded to pyfly `v26.06.103`, which serves the actuator (`/actuator/*`) and
+ the admin dashboard (`/admin`) on a dedicated management port
+ (`pyfly.management.server.port`, default `9090`) instead of the business API
+ port. flydocs now runs the API on **`8080`** (`pyfly.server.port`, was `8400`)
+ and exposes actuator/admin/health on **`9090`**:
+ - `pyfly.yaml`, `IDPSettings.port` and `FLYDOCS_PORT` default to `8080`;
+ `pyfly.management.server.port: 9090` is configured explicitly.
+ - `Dockerfile` exposes `8080` + `9090`; `docker-compose` maps both for the API
+ and exposes each worker's management port (`9091`/`9092`), and every
+ health-check now probes `:9090/actuator/health/readiness`.
+ - Worker health server (`worker_health_port`) defaults to `9090` to match.
+ - **Migration:** point load balancers / clients at `:8080` for the API and
+ Kubernetes probes / Prometheus at `:9090`. Set
+ `PYFLY_MANAGEMENT_SERVER_PORT=8080` to collapse back to a single port.
+
+- **`pyfly` is now consumed from its published GitHub tag.** `[tool.uv.sources]`
+ pins `pyfly` to `git tag v26.06.103` (matching the `fireflyframework-agentic`
+ pattern) instead of the local editable path; the dependency floor is
+ `>=26.6.103`.
+
+### Fixed
+
+- `main.py` read the removed `pyfly.web.host` key; it now reads
+ `pyfly.server.host` (Spring `server.address` parity).
+
+### Added
+
+- **Python SDK:** `Client` / `AsyncClient` accept an optional `management_url`
+ so `health()` can target the management port (`:9090`) while API calls use the
+ business `base_url` (`:8080`); back-compatible — unset falls back to `base_url`.
+
## [26.6.9] - 2026-06-15
### Fixed
diff --git a/Dockerfile b/Dockerfile
index b675495..429f097 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -162,7 +162,9 @@ RUN find /app -type f -exec chmod a+r {} + \
ENV PYTHONPATH=/app/src
USER idp
-EXPOSE 8400
+# 8080 = business API (pyfly.server.port); 9090 = management (actuator + admin,
+# pyfly.management.server.port) and the worker health server.
+EXPOSE 8080 9090
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["serve"]
diff --git a/QUICKSTART.md b/QUICKSTART.md
index bc0c4e2..4070b9a 100644
--- a/QUICKSTART.md
+++ b/QUICKSTART.md
@@ -17,13 +17,13 @@ The repo ships with a docker-compose stack that brings up the service, a Postgre
```bash
git clone https://github.com/firefly-operationOS/flydocs.git
cd flydocs
-task docker:up:test # serves http://localhost:8400 backed by a mock LLM
+task docker:up:test # serves http://localhost:8080 backed by a mock LLM
```
While it boots, verify the readiness probe:
```bash
-curl http://localhost:8400/actuator/health/readiness
+curl http://localhost:9090/actuator/health/readiness
# {"status":"UP","components":{"database_health":...,"eda_health":...}}
```
@@ -35,7 +35,7 @@ B64=$(base64 < invoice.pdf | tr -d '\n')
# 2. POST a minimal ExtractionRequest. ``document_types[]`` declares what to extract;
# ``files[]`` carries the binary. Everything else has sensible defaults.
-curl -sS http://localhost:8400/api/v1/extract \
+curl -sS http://localhost:8080/api/v1/extract \
-H 'Content-Type: application/json' \
-d @- <{@code
* flydocs:
- * base-url: http://localhost:8400 # required to enable the starter
+ * base-url: http://localhost:8080 # required to enable the starter
* webhook:
* secret: ${FLYDOCS_WEBHOOK_SECRET}
* }
*
* {@code
- * FLYDOCS_BASE_URL=http://localhost:8400 \
+ * FLYDOCS_BASE_URL=http://localhost:8080 \
* FLYDOCS_WEBHOOK_SECRET=super-secret \
* mvn -pl flydocs-examples spring-boot:run \
* -Dspring-boot.run.mainClass=com.firefly.flydocs.examples.WebhookReceiverApplication
diff --git a/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClient.java b/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClient.java
index 993e399..c0f5e96 100644
--- a/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClient.java
+++ b/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClient.java
@@ -36,7 +36,7 @@
*
* {@code
* FlydocsClient flydocs = FlydocsClient.builder()
- * .baseUrl("http://localhost:8400")
+ * .baseUrl("http://localhost:8080")
* .build();
*
* VersionInfo info = flydocs.version();
diff --git a/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClientAsync.java b/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClientAsync.java
index d113f5f..2cc9de5 100644
--- a/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClientAsync.java
+++ b/sdks/java/flydocs-sdk/src/main/java/com/firefly/flydocs/sdk/FlydocsClientAsync.java
@@ -56,7 +56,7 @@
*
* {@code
* FlydocsClientAsync flydocs = FlydocsClientAsync.builder()
- * .baseUrl("http://localhost:8400")
+ * .baseUrl("http://localhost:8080")
* .timeout(Duration.ofSeconds(60))
* .build();
*
diff --git a/sdks/java/flydocs-sdk/src/test/java/com/firefly/flydocs/sdk/integration/LiveApiIntegrationTest.java b/sdks/java/flydocs-sdk/src/test/java/com/firefly/flydocs/sdk/integration/LiveApiIntegrationTest.java
index c6f1051..0b16606 100644
--- a/sdks/java/flydocs-sdk/src/test/java/com/firefly/flydocs/sdk/integration/LiveApiIntegrationTest.java
+++ b/sdks/java/flydocs-sdk/src/test/java/com/firefly/flydocs/sdk/integration/LiveApiIntegrationTest.java
@@ -39,7 +39,7 @@
* Activate explicitly:
{@code
- * FLYDOCS_BASE_URL=http://localhost:8400 \
+ * FLYDOCS_BASE_URL=http://localhost:8080 \
* mvn -pl flydocs-sdk test -Dgroups=integration
* }
*/
diff --git a/sdks/java/flydocs-spring-boot-starter/src/main/java/com/firefly/flydocs/sdk/spring/FlydocsProperties.java b/sdks/java/flydocs-spring-boot-starter/src/main/java/com/firefly/flydocs/sdk/spring/FlydocsProperties.java
index 9f00abf..c706794 100644
--- a/sdks/java/flydocs-spring-boot-starter/src/main/java/com/firefly/flydocs/sdk/spring/FlydocsProperties.java
+++ b/sdks/java/flydocs-spring-boot-starter/src/main/java/com/firefly/flydocs/sdk/spring/FlydocsProperties.java
@@ -36,7 +36,7 @@
public class FlydocsProperties {
/**
- * Base URL of the flydocs service, e.g. {@code http://localhost:8400}.
+ * Base URL of the flydocs service, e.g. {@code http://localhost:8080}.
* Required.
*/
@Nullable
diff --git a/sdks/java/flydocs-spring-boot-starter/src/test/java/com/firefly/flydocs/sdk/spring/FlydocsAutoConfigurationTest.java b/sdks/java/flydocs-spring-boot-starter/src/test/java/com/firefly/flydocs/sdk/spring/FlydocsAutoConfigurationTest.java
index 0b988b7..54b789d 100644
--- a/sdks/java/flydocs-spring-boot-starter/src/test/java/com/firefly/flydocs/sdk/spring/FlydocsAutoConfigurationTest.java
+++ b/sdks/java/flydocs-spring-boot-starter/src/test/java/com/firefly/flydocs/sdk/spring/FlydocsAutoConfigurationTest.java
@@ -42,7 +42,7 @@ void doesNotRegisterClientsWithoutBaseUrl() {
@Test
void registersAsyncAndBlockingClientsWhenBaseUrlSet() {
runner
- .withPropertyValues("flydocs.base-url=http://localhost:8400")
+ .withPropertyValues("flydocs.base-url=http://localhost:8080")
.run(ctx -> {
assertThat(ctx).hasSingleBean(FlydocsClientAsync.class);
assertThat(ctx).hasSingleBean(FlydocsClient.class);
@@ -55,7 +55,7 @@ void registersAsyncAndBlockingClientsWhenBaseUrlSet() {
void registersWebhookVerifierWhenSecretSet() {
runner
.withPropertyValues(
- "flydocs.base-url=http://localhost:8400",
+ "flydocs.base-url=http://localhost:8080",
"flydocs.webhook.secret=super-secret")
.run(ctx -> {
assertThat(ctx).hasSingleBean(WebhookVerifier.class);
@@ -67,7 +67,7 @@ void registersWebhookVerifierWhenSecretSet() {
void honoursTimeoutAndRetryProperties() {
runner
.withPropertyValues(
- "flydocs.base-url=http://localhost:8400",
+ "flydocs.base-url=http://localhost:8080",
"flydocs.api-key=my-key",
"flydocs.timeout=30s",
"flydocs.max-attempts=3",
@@ -88,7 +88,7 @@ void honoursTimeoutAndRetryProperties() {
@Test
void userBeanOverridesAutoConfiguration() {
runner
- .withPropertyValues("flydocs.base-url=http://localhost:8400")
+ .withPropertyValues("flydocs.base-url=http://localhost:8080")
.withUserConfiguration(CustomConfig.class)
.run(ctx -> {
assertThat(ctx).hasSingleBean(FlydocsClientAsync.class);
diff --git a/sdks/python/QUICKSTART.md b/sdks/python/QUICKSTART.md
index a370eb0..3cdfea3 100644
--- a/sdks/python/QUICKSTART.md
+++ b/sdks/python/QUICKSTART.md
@@ -17,7 +17,7 @@ The SDK depends only on `httpx` and `pydantic`.
From the repo root:
```bash
-task docker:up:test # serves http://localhost:8400 backed by a mock LLM
+task docker:up:test # serves http://localhost:8080 backed by a mock LLM
```
If you already have a running flydocs deployment, point `base_url` at it and skip this step.
@@ -63,7 +63,7 @@ async def main() -> None:
# 3. Call the service. AsyncClient is the primary integration surface;
# close it as a context manager.
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
result = await flydocs.extract(request)
# 4. Read the response. v1 nests model + latency under ``result.pipeline``;
@@ -107,7 +107,7 @@ If you can't run an event loop:
```python
from flydocs_sdk import Client
-with Client("http://localhost:8400") as flydocs:
+with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(request)
```
diff --git a/sdks/python/README.md b/sdks/python/README.md
index 033f505..3c4bc52 100644
--- a/sdks/python/README.md
+++ b/sdks/python/README.md
@@ -45,7 +45,7 @@ invoice = DocumentTypeSpec(
],
)
-with Client("http://localhost:8400") as flydocs:
+with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(
ExtractionRequest(
files=[FileInput.from_path("invoice.pdf")],
@@ -77,7 +77,7 @@ from flydocs_sdk import (
)
async def main() -> None:
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
ext = await flydocs.extractions.create(
SubmitExtractionRequest(
files=[FileInput.from_path("invoice.pdf")],
diff --git a/sdks/python/TUTORIAL.md b/sdks/python/TUTORIAL.md
index 90b9e11..a57e22a 100644
--- a/sdks/python/TUTORIAL.md
+++ b/sdks/python/TUTORIAL.md
@@ -565,7 +565,7 @@ from flydocs_sdk import (
AsyncClient, ExtractionStatus, FileInput, SubmitExtractionRequest,
)
-async with AsyncClient("http://localhost:8400") as flydocs:
+async with AsyncClient("http://localhost:8080") as flydocs:
ext = await flydocs.extractions.create(
SubmitExtractionRequest(
files=[FileInput.from_path("big-batch.pdf")],
@@ -771,7 +771,12 @@ The error also carries the full RFC 7807 view via `exc.as_problem_details()` ret
**Bring your own httpx client.** `AsyncClient(..., http_client=existing)` shares your app's connection pool. The SDK never closes transports it didn't create.
-**Health checks.** `await flydocs.health("readiness")` returns the actuator JSON.
+**Health checks.** The actuator lives on the management port (`9090`), separate from the business API (`8080`). Point the client at it via `management_url`, then call `.health()`:
+
+```python
+flydocs = AsyncClient("http://localhost:8080", management_url="http://localhost:9090")
+await flydocs.health("readiness") # GET http://localhost:9090/actuator/health/readiness
+```
**Cost tracking.** When the service has cost tracking enabled, `result.pipeline.usage` carries per-agent and per-model token + USD breakdowns; webhook envelopes carry the same.
@@ -887,7 +892,7 @@ rules = [
async def main(invoice_path: str) -> None:
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
ext = await flydocs.extractions.create(
SubmitExtractionRequest(
files=[FileInput.from_path(invoice_path)],
@@ -950,7 +955,7 @@ For scripts, batch tools, and callers that can't run an event loop, `Client` wra
```python
from flydocs_sdk import Client
-with Client("http://localhost:8400") as flydocs:
+with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(req)
```
diff --git a/sdks/python/examples/01_first_extraction.py b/sdks/python/examples/01_first_extraction.py
index 6ab2975..df56fc6 100644
--- a/sdks/python/examples/01_first_extraction.py
+++ b/sdks/python/examples/01_first_extraction.py
@@ -20,7 +20,7 @@
* Walk the new response shape (``documents[*].field_groups[*].fields``).
Run from the repo root, with a flydocs service reachable at
-``http://localhost:8400`` (e.g. via ``task docker:up:test``)::
+``http://localhost:8080`` (e.g. via ``task docker:up:test``)::
uv run python sdks/python/examples/01_first_extraction.py path/to/invoice.pdf
"""
@@ -56,7 +56,7 @@ async def main(path: Path) -> int:
],
)
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
result = await flydocs.extract(
ExtractionRequest(
files=[FileInput.from_path(path)],
diff --git a/sdks/python/examples/02_typed_schema_and_rules.py b/sdks/python/examples/02_typed_schema_and_rules.py
index dbee84b..1ada44a 100644
--- a/sdks/python/examples/02_typed_schema_and_rules.py
+++ b/sdks/python/examples/02_typed_schema_and_rules.py
@@ -60,7 +60,7 @@ async def main(path: Path) -> int:
),
),
)
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
report = await flydocs.validate(req)
if not report.ok:
print("semantic validation failed:")
diff --git a/sdks/python/examples/03_async_extraction_with_wait.py b/sdks/python/examples/03_async_extraction_with_wait.py
index 851cdf9..8a743e3 100644
--- a/sdks/python/examples/03_async_extraction_with_wait.py
+++ b/sdks/python/examples/03_async_extraction_with_wait.py
@@ -51,7 +51,7 @@
async def main(path: Path) -> int:
- async with AsyncClient("http://localhost:8400", timeout=30.0) as flydocs:
+ async with AsyncClient("http://localhost:8080", timeout=30.0) as flydocs:
ext = await flydocs.extractions.create(
SubmitExtractionRequest(
files=[FileInput.from_path(path)],
diff --git a/sdks/python/examples/05_error_handling.py b/sdks/python/examples/05_error_handling.py
index dfe775d..119a340 100644
--- a/sdks/python/examples/05_error_handling.py
+++ b/sdks/python/examples/05_error_handling.py
@@ -56,7 +56,7 @@ async def main(path: Path) -> int:
files=[FileInput.from_path(path)],
document_types=[INVOICE_DOCUMENT_TYPE],
)
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
try:
result = await flydocs.extract(req)
print(f"extracted in sync: latency={result.pipeline.latency_ms}ms")
diff --git a/sdks/python/examples/06_sync_facade.py b/sdks/python/examples/06_sync_facade.py
index d677347..6b525e5 100644
--- a/sdks/python/examples/06_sync_facade.py
+++ b/sdks/python/examples/06_sync_facade.py
@@ -46,7 +46,7 @@
def main(path: Path) -> int:
- with Client("http://localhost:8400") as flydocs:
+ with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(
ExtractionRequest(
files=[FileInput.from_path(path)],
diff --git a/sdks/python/examples/README.md b/sdks/python/examples/README.md
index 6c61c4d..8240c43 100644
--- a/sdks/python/examples/README.md
+++ b/sdks/python/examples/README.md
@@ -14,7 +14,7 @@ Runnable async-first scripts exercising every capability from the [TUTORIAL](../
## Running
```bash
-task docker:up:test # spin up flydocs + mock-llm at http://localhost:8400
+task docker:up:test # spin up flydocs + mock-llm at http://localhost:8080
# Then run any example. Examples 2/3/5/6 share fixtures via PYTHONPATH:
uv run python sdks/python/examples/01_first_extraction.py path/to/invoice.pdf
diff --git a/sdks/python/src/flydocs_sdk/__init__.py b/sdks/python/src/flydocs_sdk/__init__.py
index 2363d66..f7f5797 100644
--- a/sdks/python/src/flydocs_sdk/__init__.py
+++ b/sdks/python/src/flydocs_sdk/__init__.py
@@ -50,7 +50,7 @@
],
)
- with Client("http://localhost:8400") as flydocs:
+ with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(
ExtractionRequest(
files=[FileInput.from_path("invoice.pdf")],
diff --git a/sdks/python/src/flydocs_sdk/async_client.py b/sdks/python/src/flydocs_sdk/async_client.py
index 118fd0f..5c6b705 100644
--- a/sdks/python/src/flydocs_sdk/async_client.py
+++ b/sdks/python/src/flydocs_sdk/async_client.py
@@ -80,7 +80,7 @@ class AsyncClient:
share a connection pool with the rest of your app -- the SDK will
not close transports it did not create.
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
result = await flydocs.extract(ExtractionRequest(...))
"""
@@ -89,12 +89,18 @@ def __init__(
base_url: str,
*,
api_key: str | None = None,
+ management_url: str | None = None,
timeout: float = DEFAULT_TIMEOUT_S,
default_headers: dict[str, str] | None = None,
transport: httpx.AsyncBaseTransport | None = None,
http_client: httpx.AsyncClient | None = None,
) -> None:
self._base_url = base_url.rstrip("/")
+ # The actuator (/actuator/*) and admin endpoints run on pyfly's separate
+ # management port (default 9090), not the business API port. Set
+ # ``management_url`` (e.g. "http://host:9090") so ``health()`` targets it;
+ # when unset it falls back to ``base_url`` for single-port deployments.
+ self._management_url = management_url.rstrip("/") if management_url else None
self._api_key = api_key
self._default_headers = dict(default_headers or {})
if http_client is not None:
@@ -143,9 +149,13 @@ async def health(self, probe: str = "readiness") -> dict[str, Any]:
``probe`` is typically ``readiness`` or ``liveness``. Returns
the raw actuator JSON since the shape is owned by pyfly, not
- the flydocs DTOs.
+ the flydocs DTOs. When ``management_url`` was supplied to the client the
+ request targets the management port; otherwise it uses ``base_url``.
"""
- data = await self._request_json("GET", f"/actuator/health/{probe}")
+ path = f"/actuator/health/{probe}"
+ if self._management_url is not None:
+ path = f"{self._management_url}{path}"
+ data = await self._request_json("GET", path)
if not isinstance(data, dict):
raise FlydocsClientError(f"unexpected /actuator/health/{probe} response: {data!r}")
return data
@@ -228,7 +238,7 @@ async def wait_for_completion(
:class:`TimeoutError` if the deadline elapses while the worker
is still in flight.
- async with AsyncClient("http://localhost:8400") as flydocs:
+ async with AsyncClient("http://localhost:8080") as flydocs:
ext = await flydocs.extractions.create(req)
final = await flydocs.wait_for_completion(ext.id)
if final.status == ExtractionStatus.SUCCEEDED:
diff --git a/sdks/python/src/flydocs_sdk/client.py b/sdks/python/src/flydocs_sdk/client.py
index 376caf9..7934a27 100644
--- a/sdks/python/src/flydocs_sdk/client.py
+++ b/sdks/python/src/flydocs_sdk/client.py
@@ -70,7 +70,7 @@
class Client:
"""Synchronous client over the same endpoint set as :class:`AsyncClient`.
- with Client("http://localhost:8400") as flydocs:
+ with Client("http://localhost:8080") as flydocs:
result = flydocs.extract(request)
Calling :meth:`close` (or using the context manager) shuts the
@@ -83,6 +83,7 @@ def __init__(
base_url: str,
*,
api_key: str | None = None,
+ management_url: str | None = None,
timeout: float = DEFAULT_TIMEOUT_S,
default_headers: dict[str, str] | None = None,
transport: httpx.AsyncBaseTransport | None = None,
@@ -92,6 +93,7 @@ def __init__(
self._inner = AsyncClient(
base_url,
api_key=api_key,
+ management_url=management_url,
timeout=timeout,
default_headers=default_headers,
transport=transport,
diff --git a/src/flydocs/__init__.py b/src/flydocs/__init__.py
index 3ceeffa..c4e06f6 100644
--- a/src/flydocs/__init__.py
+++ b/src/flydocs/__init__.py
@@ -24,4 +24,4 @@
`PromptRegistry`).
"""
-__version__ = "26.6.9"
+__version__ = "26.6.10"
diff --git a/src/flydocs/config.py b/src/flydocs/config.py
index fbac7f4..b2ef094 100644
--- a/src/flydocs/config.py
+++ b/src/flydocs/config.py
@@ -41,13 +41,16 @@ class IDPSettings(BaseSettings):
# -- Service --------------------------------------------------------
log_level: str = "INFO"
- port: int = 8400
+ # Business API port (pyfly serves the app here; pyfly.server.port == 8080).
+ port: int = 8080
# Port for the HTTP health server the worker CLI modes (``flydocs
# worker`` / ``flydocs bbox-worker``) run next to their asyncio tasks
- # so Kubernetes can probe ``/actuator/health/*`` over httpGet. Unset
- # reuses ``port``; ``0`` disables the server (dev setups running
- # ``serve`` and ``worker`` on the same host).
- worker_health_port: int | None = Field(default=None, ge=0, le=65535)
+ # so Kubernetes can probe ``/actuator/health/*`` over httpGet. Defaults to
+ # the management port (9090) so it matches where the ``serve`` mode exposes
+ # the actuator (pyfly.management.server.port). ``0`` disables the server
+ # (dev setups running ``serve`` and ``worker`` on the same host, where the
+ # serve mode already owns 9090).
+ worker_health_port: int | None = Field(default=9090, ge=0, le=65535)
# -- Persistence ----------------------------------------------------
database_url: str = "postgresql+asyncpg://idp:idp@localhost:5432/flydocs"
diff --git a/src/flydocs/main.py b/src/flydocs/main.py
index f50f709..be19fc7 100644
--- a/src/flydocs/main.py
+++ b/src/flydocs/main.py
@@ -72,8 +72,8 @@ async def _lifespan(app: Any):
# escape hatch for legitimate dynamic attribute writes.
setattr(_pyfly, "_route_metadata", getattr(app.state, "pyfly_route_metadata", [])) # noqa: B010
setattr(_pyfly, "_docs_enabled", getattr(app.state, "pyfly_docs_enabled", False)) # noqa: B010
- setattr(_pyfly, "_host", str(_pyfly.config.get("pyfly.web.host", "0.0.0.0"))) # noqa: B010
- setattr(_pyfly, "_port", int(_pyfly.config.get("pyfly.server.port", 8400))) # noqa: B010
+ setattr(_pyfly, "_host", str(_pyfly.config.get("pyfly.server.host", "0.0.0.0"))) # noqa: B010
+ setattr(_pyfly, "_port", int(_pyfly.config.get("pyfly.server.port", 8080))) # noqa: B010
await _pyfly.startup()
# Re-scan HealthIndicator beans now that the container has built
# every singleton (the eager scan inside ``create_app`` runs BEFORE
diff --git a/tests/unit/test_worker_health.py b/tests/unit/test_worker_health.py
index b1adf93..8c30fde 100644
--- a/tests/unit/test_worker_health.py
+++ b/tests/unit/test_worker_health.py
@@ -157,15 +157,15 @@ def test_resolve_health_port_defaults_to_service_port() -> None:
# worker_health_port passed explicitly: IDPSettings is a BaseSettings, so
# a bare constructor would absorb ambient FLYDOCS_WORKER_HEALTH_PORT from
# the developer's shell or .env and flake this assertion.
- assert resolve_health_port(IDPSettings(port=8400, worker_health_port=None)) == 8400
+ assert resolve_health_port(IDPSettings(port=8080, worker_health_port=None)) == 8080
def test_resolve_health_port_override() -> None:
- assert resolve_health_port(IDPSettings(port=8400, worker_health_port=9090)) == 9090
+ assert resolve_health_port(IDPSettings(port=8080, worker_health_port=9090)) == 9090
def test_resolve_health_port_zero_disables() -> None:
- assert resolve_health_port(IDPSettings(port=8400, worker_health_port=0)) == 0
+ assert resolve_health_port(IDPSettings(port=8080, worker_health_port=0)) == 0
# ---------------------------------------------------------------------------
@@ -174,7 +174,7 @@ def test_resolve_health_port_zero_disables() -> None:
def test_make_health_server_config() -> None:
- settings = IDPSettings(port=8400, worker_health_port=9090)
+ settings = IDPSettings(port=8080, worker_health_port=9090)
server = make_health_server(build_health_app(), settings=settings)
assert server.config.host == "0.0.0.0"
assert server.config.port == 9090
@@ -183,17 +183,17 @@ def test_make_health_server_config() -> None:
def test_make_health_server_rejects_disabled_port() -> None:
- settings = IDPSettings(port=8400, worker_health_port=0)
+ settings = IDPSettings(port=8080, worker_health_port=0)
with pytest.raises(ValueError, match="disabled"):
make_health_server(build_health_app(), settings=settings)
def test_build_worker_health_server_none_when_disabled() -> None:
- assert build_worker_health_server(None, IDPSettings(port=8400, worker_health_port=0)) is None
+ assert build_worker_health_server(None, IDPSettings(port=8080, worker_health_port=0)) is None
def test_build_worker_health_server_enabled() -> None:
- server = build_worker_health_server(None, IDPSettings(port=8400, worker_health_port=9090))
+ server = build_worker_health_server(None, IDPSettings(port=8080, worker_health_port=9090))
assert server is not None
assert server.config.port == 9090
diff --git a/uv.lock b/uv.lock
index 15f73a5..53a1388 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1483,7 +1483,7 @@ security = [
[[package]]
name = "flydocs"
-version = "26.6.9"
+version = "26.6.10"
source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
@@ -1539,7 +1539,7 @@ requires-dist = [
{ name = "pydantic", specifier = ">=2.10.0" },
{ name = "pydantic-ai-slim", extras = ["anthropic", "openai", "bedrock"], specifier = ">=1.56.0" },
{ name = "pydantic-settings", specifier = ">=2.7.0" },
- { name = "pyfly", extras = ["fastapi", "web", "observability", "security", "data-relational", "postgresql", "eda", "redis", "client", "scheduling", "cli"], editable = "../../fireflyframework/fireflyframework-pyfly" },
+ { name = "pyfly", extras = ["fastapi", "web", "observability", "security", "data-relational", "postgresql", "eda", "redis", "client", "scheduling", "cli"], git = "https://github.com/fireflyframework/fireflyframework-pyfly.git?tag=v26.06.103" },
{ name = "pymupdf", specifier = ">=1.24" },
{ name = "pypdf", specifier = ">=4.3.0" },
{ name = "pyright", marker = "extra == 'dev'", specifier = ">=1.1" },
@@ -3942,8 +3942,8 @@ wheels = [
[[package]]
name = "pyfly"
-version = "26.6.101"
-source = { editable = "../../fireflyframework/fireflyframework-pyfly" }
+version = "26.6.103"
+source = { git = "https://github.com/fireflyframework/fireflyframework-pyfly.git?tag=v26.06.103#67244e4fc23ce8bee1f40b0f819c2c8858161804" }
dependencies = [
{ name = "pydantic" },
{ name = "pyyaml" },
@@ -3999,79 +3999,6 @@ web = [
{ name = "uvicorn", extra = ["standard"] },
]
-[package.metadata]
-requires-dist = [
- { name = "aio-pika", marker = "extra == 'eda'", specifier = ">=9.6.2" },
- { name = "aio-pika", marker = "extra == 'rabbitmq'", specifier = ">=9.6.2" },
- { name = "aiokafka", marker = "extra == 'eda'", specifier = ">=0.14.0" },
- { name = "aiokafka", marker = "extra == 'kafka'", specifier = ">=0.14.0" },
- { name = "aiosqlite", marker = "extra == 'data-relational'", specifier = ">=0.22.1" },
- { name = "alembic", marker = "extra == 'data-relational'", specifier = ">=1.18.4" },
- { name = "asyncpg", marker = "extra == 'postgresql'", specifier = ">=0.31.0" },
- { name = "azure-storage-blob", marker = "extra == 'ecm-azure'", specifier = ">=12.19.0" },
- { name = "bcrypt", marker = "extra == 'security'", specifier = ">=5.0.0" },
- { name = "beanie", marker = "extra == 'data-document'", specifier = ">=2.1.0" },
- { name = "boto3", marker = "extra == 'ecm-aws'", specifier = ">=1.34.0" },
- { name = "boto3", marker = "extra == 'idp-cognito'", specifier = ">=1.34.0" },
- { name = "click", marker = "extra == 'cli'", specifier = ">=8.3.3" },
- { name = "click", marker = "extra == 'shell'", specifier = ">=8.3.3" },
- { name = "croniter", marker = "extra == 'scheduling'", specifier = ">=6.2.2" },
- { name = "cryptography", marker = "extra == 'security'", specifier = ">=48.0.0" },
- { name = "fastapi", marker = "extra == 'fastapi'", specifier = ">=0.136.1" },
- { name = "gitpython", marker = "extra == 'config-server-git'", specifier = ">=3.1" },
- { name = "granian", marker = "extra == 'granian'", specifier = ">=2.7.4" },
- { name = "grpcio", marker = "extra == 'grpc'", specifier = ">=1.60.0" },
- { name = "httpx", marker = "extra == 'client'", specifier = ">=0.28.1" },
- { name = "httpx", marker = "extra == 'idp-azure'", specifier = ">=0.28.1" },
- { name = "httpx", marker = "extra == 'idp-keycloak'", specifier = ">=0.28.1" },
- { name = "hypercorn", marker = "extra == 'hypercorn'", specifier = ">=0.18.0" },
- { name = "jinja2", marker = "extra == 'cli'", specifier = ">=3.1.6" },
- { name = "jinja2", marker = "extra == 'notifications'", specifier = ">=3.1.6" },
- { name = "jsonpath-ng", marker = "extra == 'testing'", specifier = ">=1.8.0" },
- { name = "opentelemetry-api", marker = "extra == 'observability'", specifier = ">=1.41.1" },
- { name = "opentelemetry-instrumentation-starlette", marker = "extra == 'observability'", specifier = ">=0.62b1" },
- { name = "opentelemetry-sdk", marker = "extra == 'observability'", specifier = ">=1.41.1" },
- { name = "pika", marker = "extra == 'testcontainers'", specifier = ">=1.3.0" },
- { name = "presidio-analyzer", marker = "extra == 'pii'", specifier = ">=2.2" },
- { name = "presidio-anonymizer", marker = "extra == 'pii'", specifier = ">=2.2" },
- { name = "prometheus-client", marker = "extra == 'observability'", specifier = ">=0.25.0" },
- { name = "pydantic", specifier = ">=2.13.3" },
- { name = "pyfly", extras = ["fastapi", "granian"], marker = "extra == 'web-fastapi'" },
- { name = "pyfly", extras = ["web", "data-relational", "data-document", "postgresql", "eda", "cache", "client", "grpc", "websocket", "ecm-aws", "ecm-azure", "observability", "security", "scheduling", "cli", "shell", "kafka", "rabbitmq", "redis", "granian", "fastapi", "hypercorn", "idp-azure", "idp-keycloak", "idp-cognito", "notifications", "config-server-git"], marker = "extra == 'full'" },
- { name = "pyfly", extras = ["web", "granian"], marker = "extra == 'web-fast'" },
- { name = "pyjwt", extras = ["crypto"], marker = "extra == 'security'", specifier = ">=2.12.1" },
- { name = "pyotp", marker = "extra == 'security'", specifier = ">=2.9.0" },
- { name = "python-multipart", marker = "extra == 'web'", specifier = ">=0.0.27" },
- { name = "pyyaml", specifier = ">=6.0.3" },
- { name = "questionary", marker = "extra == 'cli'", specifier = ">=2.1.1" },
- { name = "redis", extras = ["hiredis"], marker = "extra == 'cache'", specifier = ">=7.4.0" },
- { name = "redis", extras = ["hiredis"], marker = "extra == 'redis'", specifier = ">=7.4.0" },
- { name = "rich", marker = "extra == 'cli'", specifier = ">=15.0.0" },
- { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'data-relational'", specifier = ">=2.0.49" },
- { name = "starlette", marker = "extra == 'web'", specifier = ">=1.0.0" },
- { name = "structlog", marker = "extra == 'observability'", specifier = ">=25.5.0" },
- { name = "testcontainers", marker = "extra == 'testcontainers'", specifier = ">=4.0.0" },
- { name = "uvicorn", extras = ["standard"], marker = "extra == 'web'", specifier = ">=0.46.0" },
- { name = "uvloop", marker = "sys_platform != 'win32' and extra == 'web-fast'", specifier = ">=0.22.1" },
- { name = "uvloop", marker = "sys_platform != 'win32' and extra == 'web-fastapi'", specifier = ">=0.22.1" },
- { name = "websockets", marker = "extra == 'websocket'", specifier = ">=12.0" },
-]
-provides-extras = ["web", "data-relational", "testing", "testcontainers", "data-document", "postgresql", "eda", "fastapi", "granian", "hypercorn", "kafka", "rabbitmq", "redis", "cache", "client", "config-server-git", "grpc", "websocket", "idp-azure", "idp-keycloak", "idp-cognito", "ecm-aws", "ecm-azure", "observability", "scheduling", "pii", "security", "notifications", "cli", "shell", "web-fast", "web-fastapi", "full"]
-
-[package.metadata.requires-dev]
-dev = [
- { name = "aiosmtpd", specifier = ">=1.4" },
- { name = "coverage", extras = ["toml"], specifier = ">=7.13.5" },
- { name = "jsonpath-ng", specifier = ">=1.8.0" },
- { name = "mongomock-motor", specifier = ">=0.0.36" },
- { name = "mypy", specifier = ">=1.20.2" },
- { name = "pytest", specifier = ">=9.0.3" },
- { name = "pytest-asyncio", specifier = ">=1.3.0" },
- { name = "pytest-cov", specifier = ">=7.1.0" },
- { name = "respx", specifier = ">=0.21.0" },
- { name = "ruff", specifier = ">=0.15.12" },
-]
-
[[package]]
name = "pygments"
version = "2.20.0"