feat: consume Repo Manager classification publisher
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
This commit is contained in:
parent
b2f430ffb1
commit
f582773f5a
6 changed files with 201 additions and 3 deletions
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager, suppress
|
||||
|
||||
from fastapi import FastAPI, Response, status
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ from hub_core.runtime.repository_navigation import (
|
|||
from hub_core.runtime.repository_navigation_routes import (
|
||||
create_repository_navigation_router,
|
||||
)
|
||||
from hub_core.runtime.repo_manager_client import HTTPRepoProjectionClient
|
||||
from hub_core.runtime.store import InMemoryPortStore, PortStore
|
||||
from hub_core.runtime.validation import ContractValidator
|
||||
from hub_core.runtime.workload_projection import (
|
||||
|
|
@ -37,8 +39,20 @@ def create_app(
|
|||
resolved_settings = settings or RuntimeSettings.from_env()
|
||||
resolved_store = port_store or _create_store(resolved_settings)
|
||||
owns_store = port_store is None
|
||||
resolved_repo_projection_client = repo_projection_client
|
||||
owns_repo_projection_client = False
|
||||
if (
|
||||
resolved_repo_projection_client is None
|
||||
and resolved_settings.repo_manager_base_url is not None
|
||||
):
|
||||
resolved_repo_projection_client = HTTPRepoProjectionClient(
|
||||
resolved_settings.repo_manager_base_url,
|
||||
api_token=resolved_settings.repo_manager_api_token,
|
||||
timeout_seconds=resolved_settings.repo_manager_timeout_seconds,
|
||||
)
|
||||
owns_repo_projection_client = True
|
||||
repository_navigation = RepositoryNavigationService(
|
||||
client=repo_projection_client,
|
||||
client=resolved_repo_projection_client,
|
||||
store=resolved_store,
|
||||
)
|
||||
workload_projection = WorkloadProjectionService(
|
||||
|
|
@ -48,19 +62,33 @@ def create_app(
|
|||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_: FastAPI):
|
||||
if repo_projection_client is not None:
|
||||
refresh_task: asyncio.Task[None] | None = None
|
||||
if resolved_repo_projection_client is not None:
|
||||
try:
|
||||
await repository_navigation.refresh()
|
||||
except ProjectionRejected:
|
||||
# The readiness dependency reports the rejected or absent
|
||||
# projection while the API remains available for diagnosis.
|
||||
pass
|
||||
if resolved_settings.repo_projection_refresh_seconds > 0:
|
||||
refresh_task = asyncio.create_task(
|
||||
_refresh_repository_projection(
|
||||
repository_navigation,
|
||||
resolved_settings.repo_projection_refresh_seconds,
|
||||
)
|
||||
)
|
||||
if workload_projection_client is not None:
|
||||
try:
|
||||
await workload_projection.refresh()
|
||||
except WorkloadProjectionRejected:
|
||||
pass
|
||||
yield
|
||||
if refresh_task is not None:
|
||||
refresh_task.cancel()
|
||||
with suppress(asyncio.CancelledError):
|
||||
await refresh_task
|
||||
if owns_repo_projection_client:
|
||||
await resolved_repo_projection_client.aclose() # type: ignore[union-attr]
|
||||
if owns_store and (closer := getattr(resolved_store, "aclose", None)):
|
||||
await closer()
|
||||
|
||||
|
|
@ -117,6 +145,19 @@ def create_app(
|
|||
return app
|
||||
|
||||
|
||||
async def _refresh_repository_projection(
|
||||
service: RepositoryNavigationService,
|
||||
interval_seconds: float,
|
||||
) -> None:
|
||||
while True:
|
||||
await asyncio.sleep(interval_seconds)
|
||||
try:
|
||||
await service.refresh()
|
||||
except ProjectionRejected:
|
||||
# The service records stale state and diagnostics; retry on schedule.
|
||||
pass
|
||||
|
||||
|
||||
def _create_store(settings: RuntimeSettings) -> PortStore:
|
||||
if settings.backend == "memory":
|
||||
return InMemoryPortStore()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue