9993c96907
Move provider auth and OAuth flows onto pi-ai Models, compose models.json and extension overlays through ModelRuntime, and retain ModelRegistry as an extension compatibility facade.
127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
import type { ModelRuntime } from "./model-runtime.ts";
|
|
import type { AuthStatus, ProviderConfigInput } from "./provider-composer.ts";
|
|
|
|
export type { ProviderConfigInput } from "./provider-composer.ts";
|
|
export type ResolvedRequestAuth =
|
|
| {
|
|
ok: true;
|
|
apiKey?: string;
|
|
headers?: Record<string, string>;
|
|
env?: Record<string, string>;
|
|
}
|
|
| { ok: false; error: string };
|
|
export { clearApiKeyCache } from "./provider-composer.ts";
|
|
|
|
/**
|
|
* Synchronous compatibility facade exposed to extensions.
|
|
* Coding-agent internals use ModelRuntime directly.
|
|
*/
|
|
export class ModelRegistry {
|
|
private readonly runtime: ModelRuntime;
|
|
|
|
constructor(runtime: ModelRuntime) {
|
|
this.runtime = runtime;
|
|
}
|
|
|
|
/** Reload models.json asynchronously. Await before making synchronous registry reads. */
|
|
refresh(): Promise<void> {
|
|
return this.runtime.reloadConfig();
|
|
}
|
|
|
|
getError(): string | undefined {
|
|
return this.runtime.getError();
|
|
}
|
|
|
|
getAll(): Model<Api>[] {
|
|
return [...this.runtime.getModels()];
|
|
}
|
|
|
|
getAvailable(): Model<Api>[] {
|
|
return [...this.runtime.getAvailableSnapshot()];
|
|
}
|
|
|
|
find(provider: string, modelId: string): Model<Api> | undefined {
|
|
return this.runtime.getModel(provider, modelId);
|
|
}
|
|
|
|
hasConfiguredAuth(model: Model<Api>): boolean {
|
|
return this.runtime.hasConfiguredAuth(model.provider);
|
|
}
|
|
|
|
async getApiKeyAndHeaders(model: Model<Api>): Promise<ResolvedRequestAuth> {
|
|
try {
|
|
const resolution = await this.runtime.getAuth(model);
|
|
if (!resolution) {
|
|
const compatibility = this.runtime.getCompatibilityRequestConfig(model);
|
|
if (compatibility.authHeader) {
|
|
return { ok: false, error: `No API key found for "${model.provider}"` };
|
|
}
|
|
const headers = compatibility.headers
|
|
? Object.fromEntries(
|
|
Object.entries(compatibility.headers).filter(
|
|
(entry): entry is [string, string] => entry[1] !== null,
|
|
),
|
|
)
|
|
: undefined;
|
|
return { ok: true, headers };
|
|
}
|
|
const headers = resolution.auth.headers
|
|
? Object.fromEntries(
|
|
Object.entries(resolution.auth.headers).filter(
|
|
(entry): entry is [string, string] => entry[1] !== null,
|
|
),
|
|
)
|
|
: undefined;
|
|
return { ok: true, apiKey: resolution.auth.apiKey, headers, env: resolution.env };
|
|
} catch (error) {
|
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
const message =
|
|
cause instanceof Error ? cause.message : error instanceof Error ? error.message : String(error);
|
|
return {
|
|
ok: false,
|
|
error:
|
|
message === "authHeader requires a resolved API key"
|
|
? `No API key found for "${model.provider}"`
|
|
: message,
|
|
};
|
|
}
|
|
}
|
|
|
|
getProviderAuthStatus(provider: string): AuthStatus {
|
|
return this.runtime.getProviderAuthStatus(provider);
|
|
}
|
|
|
|
getProviderDisplayName(provider: string): string {
|
|
return this.runtime.getProvider(provider)?.name ?? provider;
|
|
}
|
|
|
|
async getApiKeyForProvider(provider: string): Promise<string | undefined> {
|
|
try {
|
|
return (await this.runtime.getAuth(provider))?.auth.apiKey;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
isUsingOAuth(model: Model<Api>): boolean {
|
|
return this.runtime.isUsingOAuth(model.provider);
|
|
}
|
|
|
|
registerProvider(providerName: string, config: ProviderConfigInput): void {
|
|
this.runtime.registerProvider(providerName, config);
|
|
}
|
|
|
|
unregisterProvider(providerName: string): void {
|
|
this.runtime.unregisterProvider(providerName);
|
|
}
|
|
|
|
getRegisteredProviderConfig(providerName: string): ProviderConfigInput | undefined {
|
|
return this.runtime.getRegisteredProviderConfig(providerName);
|
|
}
|
|
|
|
getRegisteredProviderIds(): readonly string[] {
|
|
return this.runtime.getRegisteredProviderIds();
|
|
}
|
|
}
|