Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
132 lines
5.0 KiB
TypeScript
132 lines
5.0 KiB
TypeScript
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
import type { ModelConnectInput, ModelCredential, ThinkingLevel } from "@rakazo/contracts";
|
|
import { OPENAI_COMPATIBLE_PROVIDER_ID as CONTRACT_OPENAI_COMPAT } from "@rakazo/contracts";
|
|
import { modelIdSupportsImages, updateModelImageCapabilities } from "./model-vision.js";
|
|
import { parseModelSecret, type StoredModelSecret, serializeModelSecret } from "./pi-oauth.js";
|
|
import {
|
|
OPENAI_COMPATIBLE_PROVIDER_ID,
|
|
openAiCompatibleModel,
|
|
prepareOpenAiCompatibleConnect,
|
|
} from "./pi-openai-compatible-provider.js";
|
|
|
|
export type BuildModelConnectOptions = {
|
|
/** Skip writing visionModelIds when prior plaintext was unavailable during key replacement. */
|
|
omitVisionModelIds?: boolean;
|
|
};
|
|
|
|
export function buildModelConnectPlaintext(
|
|
input: ModelConnectInput,
|
|
previousPlaintext?: string,
|
|
options?: BuildModelConnectOptions,
|
|
): string {
|
|
if (input.provider === OPENAI_COMPATIBLE_PROVIDER_ID) {
|
|
const prepared = prepareOpenAiCompatibleConnect(input);
|
|
const previous = previousPlaintext ? parseModelSecret(previousPlaintext) : undefined;
|
|
const sameEndpoint =
|
|
previous?.kind === "openai_compatible" && previous.baseUrl === prepared.baseUrl;
|
|
if (input.apiKey === undefined && sameEndpoint) {
|
|
// Revalidate the inherited key too: public endpoints must still use HTTPS.
|
|
prepared.apiKey = prepareOpenAiCompatibleConnect({
|
|
...input,
|
|
apiKey: previous.apiKey,
|
|
}).apiKey;
|
|
}
|
|
const previousVisionModelIds = sameEndpoint ? previous.visionModelIds : undefined;
|
|
const maxImagesPerPrompt =
|
|
input.maxImagesPerPrompt === null
|
|
? undefined
|
|
: (input.maxImagesPerPrompt ?? (sameEndpoint ? previous.maxImagesPerPrompt : undefined));
|
|
const thinkingLevel =
|
|
input.thinkingLevel !== undefined
|
|
? input.thinkingLevel
|
|
: sameEndpoint
|
|
? previous.thinkingLevel
|
|
: undefined;
|
|
const maxTokens =
|
|
input.maxTokens !== undefined
|
|
? input.maxTokens
|
|
: sameEndpoint
|
|
? previous.maxTokens
|
|
: undefined;
|
|
const contextWindow =
|
|
input.contextWindow !== undefined
|
|
? input.contextWindow
|
|
: sameEndpoint
|
|
? previous.contextWindow
|
|
: undefined;
|
|
const visionModelIds = updateModelImageCapabilities(
|
|
previousVisionModelIds,
|
|
prepared.modelId,
|
|
input.supportsImages,
|
|
);
|
|
const includeVisionModelIds =
|
|
!options?.omitVisionModelIds &&
|
|
(input.supportsImages !== undefined || previousVisionModelIds !== undefined);
|
|
const secret: StoredModelSecret = {
|
|
kind: "openai_compatible",
|
|
baseUrl: prepared.baseUrl,
|
|
...(input.reasoning !== undefined ? { reasoning: input.reasoning } : {}),
|
|
...(thinkingLevel !== undefined ? { thinkingLevel } : {}),
|
|
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
...(prepared.apiKey ? { apiKey: prepared.apiKey } : {}),
|
|
...(includeVisionModelIds ? { visionModelIds } : {}),
|
|
...(maxImagesPerPrompt !== undefined ? { maxImagesPerPrompt } : {}),
|
|
};
|
|
return serializeModelSecret(secret);
|
|
}
|
|
const apiKey = input.apiKey?.trim();
|
|
if (!apiKey || apiKey.length < 1) {
|
|
throw new Error("API key must contain at least 8 characters");
|
|
}
|
|
return apiKey;
|
|
}
|
|
|
|
export function modelCredentialDto(
|
|
row: {
|
|
id: string;
|
|
provider: string;
|
|
label: string;
|
|
isDefault: boolean;
|
|
defaultModel?: string | null;
|
|
supportsImages?: boolean;
|
|
},
|
|
plaintext?: string,
|
|
): ModelCredential {
|
|
const credential: ModelCredential = {
|
|
id: row.id,
|
|
provider: row.provider,
|
|
label: row.label,
|
|
hasKey: true,
|
|
isDefault: row.isDefault,
|
|
...(row.defaultModel ? { modelId: row.defaultModel } : {}),
|
|
};
|
|
if (row.provider !== CONTRACT_OPENAI_COMPAT) return credential;
|
|
const compatibleCredential = {
|
|
...credential,
|
|
supportsImages: row.supportsImages ?? false,
|
|
};
|
|
if (!plaintext) return compatibleCredential;
|
|
const parsed = parseModelSecret(plaintext);
|
|
if (parsed.kind !== "openai_compatible") return compatibleCredential;
|
|
return {
|
|
...compatibleCredential,
|
|
supportsImages:
|
|
parsed.visionModelIds !== undefined
|
|
? modelIdSupportsImages(parsed.visionModelIds, row.defaultModel)
|
|
: compatibleCredential.supportsImages,
|
|
baseUrl: parsed.baseUrl,
|
|
reasoning: parsed.reasoning ?? false,
|
|
...(parsed.thinkingLevel !== undefined ? { thinkingLevel: parsed.thinkingLevel } : {}),
|
|
...(parsed.maxTokens !== undefined ? { maxTokens: parsed.maxTokens } : {}),
|
|
...(parsed.contextWindow !== undefined ? { contextWindow: parsed.contextWindow } : {}),
|
|
...(parsed.maxImagesPerPrompt !== undefined
|
|
? { maxImagesPerPrompt: parsed.maxImagesPerPrompt }
|
|
: {}),
|
|
thinkingLevels: getSupportedThinkingLevels(
|
|
openAiCompatibleModel(row.defaultModel ?? "custom", parsed.baseUrl, parsed.reasoning),
|
|
) as ThinkingLevel[],
|
|
modelId: row.defaultModel ?? undefined,
|
|
};
|
|
}
|