blob: 865673c01704b071924f6c6088ca9f85de2d65e5 [file]
import { authorizeConnectionModel, effectiveBaseUrl } from '@maka/core/llm-connections';
import { readRuntimeHostConnectionCatalog } from './catalog-reader.js';
import type { RuntimeHostConnection } from './connection.js';
import { abortable } from './wait-for-ready.js';
type TargetConnection = Pick<RuntimeHostConnection, 'request'>;
export interface HostedExecutionTargetInput {
readonly connectionSlug: string;
readonly model: string;
readonly baseUrl: string;
}
export async function configureHostedExecutionTarget(
connection: TargetConnection,
input: HostedExecutionTargetInput,
signal?: AbortSignal,
): Promise<boolean> {
const before = await abortable(() => readRuntimeHostConnectionCatalog(connection), signal);
const target = before.connections.find((candidate) => candidate.slug === input.connectionSlug);
if (!target) throw new Error('Runtime Host connection is unavailable');
const baseUrl = new URL(input.baseUrl).toString();
const enabledModelIds = [...new Set([...target.enabledModelIds, input.model])];
const endpointChanged = canonicalBaseUrl(effectiveBaseUrl(target)) !== baseUrl;
let changed = false;
if (endpointChanged || !target.enabled || !target.enabledModelIds.includes(input.model)) {
const updated = await abortable(
() =>
connection.request('connection.catalog.update', {
expected: { connectionId: target.connectionId, revision: target.revision },
changes: {
name: target.name,
baseUrl,
enabled: true,
enabledModelIds,
},
}),
signal,
);
if (updated.kind !== 'committed') {
throw new Error(`Runtime Host connection update was not committed: ${updated.kind}`);
}
changed = true;
}
// Best-effort: a fetch here is how the target picks up wire metadata for a
// model the catalog has not described yet, so it is worth trying. It is not
// worth failing over. A provider with no model-list endpoint refuses the
// operation outright, one whose list lags simply returns the same array, and
// in both cases the user's selection still authorizes the model — the
// admission check below is what decides.
if (endpointChanged || !target.models.some((model) => model.id === input.model)) {
const fetched = await abortable(
() =>
connection.request('connection.models.fetch', {
connectionId: target.connectionId,
}),
signal,
);
if (fetched.kind === 'committed') changed = true;
}
const after = await abortable(() => readRuntimeHostConnectionCatalog(connection), signal);
const configured = after.connections.find(
(candidate) => candidate.connectionId === target.connectionId,
);
if (
!configured?.enabled ||
canonicalBaseUrl(effectiveBaseUrl(configured)) !== baseUrl ||
!authorizeConnectionModel(configured, input.model)
) {
throw new Error('Runtime Host did not admit the requested model target');
}
return changed;
}
function canonicalBaseUrl(value: string): string | undefined {
try {
return new URL(value).toString();
} catch {
return undefined;
}
}