diff --git a/src/api/config-service/config-service.api.ts b/src/api/config-service/config-service.api.ts index 0aefa0490c..3953a6d56e 100644 --- a/src/api/config-service/config-service.api.ts +++ b/src/api/config-service/config-service.api.ts @@ -1,5 +1,7 @@ import { LLMMetadataClient } from "@openhands/typescript-client/clients"; import { getAgentServerClientOptions } from "../agent-server-client-options"; +import { getActiveBackend } from "../backend-registry/active-store"; +import { callCloudProxy } from "../cloud/proxy"; import type { LLMModel, LLMModelPage, @@ -42,11 +44,47 @@ function limitItems(items: T[], limit?: number): T[] { return items.slice(0, limit); } +function buildCloudQueryString( + params: Record, +): string { + const qs = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + if (value !== undefined) qs.set(key, String(value)); + } + const str = qs.toString(); + return str ? `?${str}` : ""; +} + class ConfigService { + /** + * @param verifiedByProvider - Pre-fetched verified-models map used by the + * local reconstruction path. Ignored for cloud backends, which call + * `/api/v1/config/models/search` directly (verified status is embedded in + * each returned item). + */ static async searchModels( params: SearchModelsParams = {}, verifiedByProvider?: Record, ): Promise { + const active = getActiveBackend(); + + if (active.backend.kind === "cloud") { + // Cloud exposes /api/v1/config/models/search which returns LLMModelPage directly. + // verifiedByProvider is not needed — the cloud API embeds verified status natively. + const qs = buildCloudQueryString({ + page_id: params.page_id, + limit: params.limit, + query: params.query, + verified__eq: params.verified__eq, + provider__eq: params.provider__eq, + }); + return callCloudProxy({ + backend: active.backend, + method: "GET", + path: `/api/v1/config/models/search${qs}`, + }); + } + const llmClient = new LLMMetadataClient(getAgentServerClientOptions()); const verifiedFetch = verifiedByProvider !== undefined @@ -87,16 +125,37 @@ class ConfigService { params.limit, ); - return { - items, - next_page_id: null, - }; + return { items, next_page_id: null }; } + /** + * @param verifiedByProvider - Pre-fetched verified-models map used by the + * local reconstruction path. Ignored for cloud backends, which call + * `/api/v1/config/providers/search` directly (verified status is embedded in + * each returned item). + */ static async searchProviders( params: SearchProvidersParams = {}, verifiedByProvider?: Record, ): Promise { + const active = getActiveBackend(); + + if (active.backend.kind === "cloud") { + // Cloud exposes /api/v1/config/providers/search which returns ProviderPage directly. + // verifiedByProvider is not needed — the cloud API embeds verified status natively. + const qs = buildCloudQueryString({ + page_id: params.page_id, + limit: params.limit, + query: params.query, + verified__eq: params.verified__eq, + }); + return callCloudProxy({ + backend: active.backend, + method: "GET", + path: `/api/v1/config/providers/search${qs}`, + }); + } + const llmClient = new LLMMetadataClient(getAgentServerClientOptions()); const verifiedFetch = verifiedByProvider !== undefined @@ -122,10 +181,7 @@ class ConfigService { params.limit, ); - return { - items, - next_page_id: null, - }; + return { items, next_page_id: null }; } } diff --git a/src/hooks/query/use-verified-models.ts b/src/hooks/query/use-verified-models.ts index 438049ce24..cf0027286d 100644 --- a/src/hooks/query/use-verified-models.ts +++ b/src/hooks/query/use-verified-models.ts @@ -1,5 +1,6 @@ import { LLMMetadataClient } from "@openhands/typescript-client/clients"; import { getAgentServerClientOptions } from "#/api/agent-server-client-options"; +import { getActiveBackend } from "#/api/backend-registry/active-store"; export const VERIFIED_MODELS_QUERY_KEY = ["config", "verified-models"] as const; export const VERIFIED_MODELS_STALE_TIME = 1000 * 60 * 5; @@ -8,6 +9,14 @@ export const VERIFIED_MODELS_GC_TIME = 1000 * 60 * 15; export async function fetchVerifiedModelsByProvider(): Promise< Record > { + const active = getActiveBackend(); + if (active.backend.kind === "cloud") { + // Cloud backends use /api/v1/config/providers/search and /api/v1/config/models/search, + // which return verified status directly on each item. The intermediate + // verifiedByProvider map is only used by the local ConfigService reconstruction + // logic, so callers can safely treat this empty object as a no-op for cloud. + return {}; + } const client = new LLMMetadataClient(getAgentServerClientOptions()); return (await client.getVerifiedModels()) ?? {}; }