feat(cloud): manage LLM providers in database
Tests / Test passed: 664

This commit is contained in:
2026-07-14 00:31:47 +08:00
parent b613a315ff
commit a166ffd8a4
35 changed files with 2432 additions and 204 deletions
@@ -39,7 +39,9 @@ from cloud.internal_api.models import (
TerminalResultRequest,
TerminalResultResponse,
)
from cloud.planner_config import build_cloud_planner_client, load_cloud_planner_config
from cloud.llm_providers import LlmProviderResolutionError, LlmProviderService
from cloud.planner_config import build_cloud_planner_client
from cloud.provider_secrets import ProviderSecretConfigurationError
from cloud.repository import (
DeviceEnrollmentConflictError,
HostEnrollmentConflictError,
@@ -65,6 +67,7 @@ def create_internal_router(
lease_duration_seconds: float = 60.0,
sleep: Callable[[float], Awaitable[None]] = asyncio.sleep,
planner_client_factory: Callable[[], ToolCallingClient] | None = None,
planner_provider_service: LlmProviderService | None = None,
scheduler: TaskScheduler | None = None,
planner_token_reservation_ceiling: int = 4096,
planner_token_reservation_ttl_seconds: float = 300.0,
@@ -78,8 +81,6 @@ def create_internal_router(
if planner_token_reservation_ttl_seconds <= 0:
raise ValueError("planner_token_reservation_ttl_seconds must be greater than zero")
router = APIRouter(prefix=version_prefix, tags=["host-agent"])
build_planner_client = planner_client_factory or _default_planner_client_factory
def authorize_host(request: Request, host_id: str) -> None:
principal = auth_provider.authenticate(request)
if principal is None:
@@ -395,6 +396,28 @@ def create_internal_router(
for tool in payload.tools
]
try:
if planner_client_factory is not None:
client = planner_client_factory()
resolved_provider = "test"
resolved_model = "test"
elif planner_provider_service is not None:
resolved = planner_provider_service.resolve_active_profile()
client = build_cloud_planner_client(resolved)
resolved_provider = resolved.profile.provider_type
resolved_model = resolved.profile.model
else:
raise LlmProviderResolutionError("no database Provider resolver configured")
except (LlmProviderResolutionError, ProviderSecretConfigurationError) as exc:
logger.info(
"planner-decision request failed",
extra={"host_id": host_id, "error_class": type(exc).__name__},
)
return JSONResponse(
status_code=status.HTTP_502_BAD_GATEWAY,
content=PlannerDecisionError(detail=str(exc)).model_dump(),
)
now = utc_now()
reservation = None
try:
@@ -415,7 +438,6 @@ def create_internal_router(
)
started_at = monotonic()
client = build_planner_client()
try:
decision = client.decide(
system_prompt=payload.system_prompt,
@@ -439,12 +461,11 @@ def create_internal_router(
)
usage = decision.usage
if reservation is not None and usage is not None and usage.total_tokens is not None:
planner_config = load_cloud_planner_config()
pool.store.settle_host_token_reservation(
reservation_id=reservation.id,
event_id=uuid4().hex,
provider=planner_config.provider,
model=planner_config.resolved_model(),
provider=resolved_provider,
model=resolved_model,
input_tokens=usage.input_tokens,
output_tokens=usage.output_tokens,
total_tokens=usage.total_tokens,
@@ -469,10 +490,6 @@ def create_internal_router(
return router
def _default_planner_client_factory() -> ToolCallingClient:
return build_cloud_planner_client(load_cloud_planner_config())
def _validate_assignment_identity(
*,
host_id: str,