mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 03:42:21 +03:00
feat(volcengine): live model discovery for Ark plan providers
Replace the static curated model lists for volcengine-agent-plan and
volcengine-coding-plan with live discovery from the console APIs
(GetAgentPlanModelMappingMeta / ListArkCodeLatestModel), authenticated by
the console cookie+csrf already captured at plan binding time.
- Add volcenginePlanModelDiscovery.ts: fetch + parse + capability enrichment
(family->contextLength/vision/reasoning map, conservative default fallback).
Console calls go through a dynamic undici import to bypass OmniRoute's
global fetch patch (built for LLM provider traffic, reroutes console hits).
Coding plan's ListArkCodeLatestModel needs {AccountId:<number>} extracted
from the console cookie; agent plan's GetAgentPlanModelMappingMeta filters
PlatformAllowStatus===true && Type==='llm'.
- Remove both plan ids from CURATED_MODEL_ONLY_PROVIDERS so synced models
merge into /v1/models and the dashboard Sync Models button works.
- sync-models route: short-circuit to console discovery for plan providers
(the chat API has no /models endpoint); persist via
replaceSyncedAvailableModelsForConnection.
- volcenginePlanBinding: set autoSync:true on new plan connections so the
24h modelSyncScheduler refreshes them automatically.
- volcPlanAutoSyncBackfill: idempotent boot-time backfill so pre-existing
plan connections also enter the scheduler.
Verified end-to-end on local OmniRoute build against live Volcano console:
agent plan synced 7 LLMs, coding plan synced 11 models, /v1/models exposes
all of them (incl. new glm-5-3-260801 / deepseek-v4-flash-260801).
This commit is contained in:
committed by
Markus Hartung
parent
d732cf615d
commit
76ac1c8b7e
@@ -21,6 +21,11 @@ import {
|
||||
import { autoSyncCodexProfilesFromLiveCatalog } from "@/lib/cli-helper/codexProfileAutoSync";
|
||||
import { autoSyncClaudeProfilesFromLiveCatalog } from "@/lib/cli-helper/claudeProfileAutoSync";
|
||||
import { providerUsesCuratedModelsOnly } from "@/lib/providers/modelListingCapability";
|
||||
import {
|
||||
fetchVolcPlanModels,
|
||||
providerToVolcPlanKind,
|
||||
} from "@/lib/providers/volcenginePlanModelDiscovery";
|
||||
import { replaceSyncedAvailableModelsForConnection } from "@/lib/db/models";
|
||||
import { GET as getProviderModels } from "../models/route";
|
||||
import { isDegradedDiscovery } from "./degradedLocalCatalog";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
|
||||
@@ -423,6 +428,84 @@ export async function POST(request: Request, { params }: { params: Promise<{ id:
|
||||
|
||||
logProvider = toNonEmptyString(connection.provider) || "unknown";
|
||||
channelLabel = getModelSyncChannelLabel(connection);
|
||||
|
||||
// Volcano Ark plan providers: discover models live from the console API
|
||||
// (cookie+csrf captured at bind time). The chat API has no /models
|
||||
// endpoint, so the default discovery path below cannot serve them.
|
||||
const volcPlanKind = providerToVolcPlanKind(logProvider);
|
||||
if (volcPlanKind) {
|
||||
const psd =
|
||||
connection.providerSpecificData && typeof connection.providerSpecificData === "object"
|
||||
? (connection.providerSpecificData as JsonRecord)
|
||||
: {};
|
||||
const cookie = toNonEmptyString(psd.volcConsoleCookie) || "";
|
||||
const csrf = toNonEmptyString(psd.volcCsrfToken) || "";
|
||||
const duration = Date.now() - start;
|
||||
let discovered;
|
||||
try {
|
||||
discovered = await fetchVolcPlanModels(volcPlanKind, cookie, csrf);
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
await saveCallLog({
|
||||
method: "POST",
|
||||
path: `/api/providers/${id}/sync-models`,
|
||||
status: 401,
|
||||
model: "model-sync",
|
||||
provider: logProvider,
|
||||
sourceFormat: "-",
|
||||
connectionId: id,
|
||||
duration,
|
||||
error: message,
|
||||
requestType: "model-sync",
|
||||
...(channelLabel ? { responseBody: { channel: channelLabel } } : {}),
|
||||
}).catch(() => undefined);
|
||||
return NextResponse.json(
|
||||
{ error: sanitizeErrorMessage(message) || "Volcano plan discovery failed" },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
const previous = await getSyncedAvailableModelsForConnection(logProvider, id);
|
||||
const synced = await replaceSyncedAvailableModelsForConnection(logProvider, id, discovered);
|
||||
const prevIds = new Set(previous.map((m) => String(m.id)));
|
||||
const added = synced.filter((m) => !prevIds.has(String(m.id))).length;
|
||||
const removed = previous.filter(
|
||||
(m) => !synced.some((n) => String(n.id) === String(m.id))
|
||||
).length;
|
||||
await saveCallLog({
|
||||
method: "GET",
|
||||
path: `/api/providers/${id}/models`,
|
||||
status: 200,
|
||||
model: "model-sync",
|
||||
provider: logProvider,
|
||||
sourceFormat: "console-discovery",
|
||||
connectionId: id,
|
||||
duration: Date.now() - start,
|
||||
requestType: "model-sync",
|
||||
responseBody: {
|
||||
source: "volcengine-plan-console-discovery",
|
||||
plan: volcPlanKind,
|
||||
syncedModels: synced.length,
|
||||
added,
|
||||
removed,
|
||||
provider: logProvider,
|
||||
channel: channelLabel,
|
||||
mode,
|
||||
},
|
||||
}).catch(() => undefined);
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
provider: logProvider,
|
||||
connectionId: id,
|
||||
source: "volcengine-plan-console-discovery",
|
||||
plan: volcPlanKind,
|
||||
mode,
|
||||
syncedModels: synced.length,
|
||||
availableModelsCount: synced.length,
|
||||
modelChanges: { added, removed, total: added + removed },
|
||||
models: synced,
|
||||
});
|
||||
}
|
||||
|
||||
if (providerUsesCuratedModelsOnly(logProvider)) {
|
||||
const [removedSyncedLists, removedImportedModelIds] = await Promise.all([
|
||||
deleteSyncedAvailableModelsForProvider(logProvider),
|
||||
|
||||
Reference in New Issue
Block a user