|
|
@@ -0,0 +1,246 @@
|
|
|
+import type { V1ObjectMeta } from '@kubernetes/client-node';
|
|
|
+
|
|
|
+import {
|
|
|
+ type ExternalModel,
|
|
|
+ type ManagedModel,
|
|
|
+ type Model,
|
|
|
+ MODEL_MODALITIES,
|
|
|
+ type ModelBase,
|
|
|
+ type ModelModality,
|
|
|
+} from '$lib/types/entities';
|
|
|
+import * as V1alpha1ExternalModel from '$lib/types/locostack.com/v1alpha1/ExternalModel';
|
|
|
+import * as V1alpha1ManagedModel from '$lib/types/locostack.com/v1alpha1/ManagedModel';
|
|
|
+
|
|
|
+import {
|
|
|
+ fromAuth,
|
|
|
+ fromTemplate,
|
|
|
+ parseCsv,
|
|
|
+ parseIntString,
|
|
|
+ resourceLabels,
|
|
|
+ toAuth,
|
|
|
+ toTemplate,
|
|
|
+} from './types';
|
|
|
+
|
|
|
+type ExternalModelCR = Omit<
|
|
|
+ V1alpha1ExternalModel.components['schemas']['ExternalModel'],
|
|
|
+ 'metadata'
|
|
|
+> & {
|
|
|
+ metadata: V1ObjectMeta;
|
|
|
+};
|
|
|
+
|
|
|
+type ManagedModelCR = Omit<
|
|
|
+ V1alpha1ManagedModel.components['schemas']['ManagedModel'],
|
|
|
+ 'metadata'
|
|
|
+> & {
|
|
|
+ metadata: V1ObjectMeta;
|
|
|
+};
|
|
|
+
|
|
|
+const modelModalitySet = new Set(MODEL_MODALITIES.map((m) => m.value));
|
|
|
+
|
|
|
+const parseModelModalities = (value: string | undefined): ModelModality[] | undefined => {
|
|
|
+ const values = parseCsv(value);
|
|
|
+ if (!values) {
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
+ const modalities = values.filter((v): v is ModelModality => modelModalitySet.has(v));
|
|
|
+ return modalities.length > 0 ? modalities : undefined;
|
|
|
+};
|
|
|
+
|
|
|
+const modelLabels = (model: ModelBase): { [key: string]: string } =>
|
|
|
+ resourceLabels(model.stack, 'Model', model.id);
|
|
|
+
|
|
|
+const modelAnnotation = (key: string, value: string | undefined): { [key: string]: string } =>
|
|
|
+ value
|
|
|
+ ? {
|
|
|
+ [`models.locostack.com/${key}`]: value,
|
|
|
+ }
|
|
|
+ : {};
|
|
|
+
|
|
|
+const modelAnnotations = (model: ModelBase): { [key: string]: string } => ({
|
|
|
+ ...modelAnnotation('icon', model.icon),
|
|
|
+ ...modelAnnotation('description', model.description),
|
|
|
+ ...modelAnnotation('tags', model.tags?.join(',')),
|
|
|
+ ...modelAnnotation('family', model.info.family),
|
|
|
+ ...modelAnnotation('baseModelId', model.info.baseModelId),
|
|
|
+ ...modelAnnotation('releaseDate', model.info.releaseDate),
|
|
|
+ ...modelAnnotation('parameterCount', model.info.parameterCount?.toString()),
|
|
|
+ ...modelAnnotation('contextWindow', model.info.contextWindow?.toString()),
|
|
|
+ ...modelAnnotation('inputs', model.info.inputs?.join(',')),
|
|
|
+ ...modelAnnotation('outputs', model.info.outputs?.join(',')),
|
|
|
+ ...modelAnnotation('embeddingDimensions', model.info.embeddingDimensions?.toString()),
|
|
|
+ ...modelAnnotation('maxInputTokens', model.info.maxInputTokens?.toString()),
|
|
|
+ ...modelAnnotation('languages', model.info.languages?.join(',')),
|
|
|
+});
|
|
|
+
|
|
|
+const readModelAnnotation = (metadata: V1ObjectMeta, key: string): string | undefined =>
|
|
|
+ metadata.annotations?.[`models.locostack.com/${key}`];
|
|
|
+
|
|
|
+const parseModelBase = (cr: ExternalModelCR | ManagedModelCR): ModelBase => {
|
|
|
+ const spec = cr.spec;
|
|
|
+ const metadata = cr.metadata;
|
|
|
+ const id = metadata.name;
|
|
|
+ if (id === undefined) {
|
|
|
+ throw new Error('metadata.name is required');
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ id,
|
|
|
+ stack: spec.stackRef?.name,
|
|
|
+ name: spec.modelName,
|
|
|
+ icon: readModelAnnotation(metadata, 'icon'),
|
|
|
+ description: readModelAnnotation(metadata, 'description') ?? '',
|
|
|
+ tags: parseCsv(readModelAnnotation(metadata, 'tags')) ?? [],
|
|
|
+ category: spec.category,
|
|
|
+ info: {
|
|
|
+ family: readModelAnnotation(metadata, 'family'),
|
|
|
+ baseModelId: readModelAnnotation(metadata, 'baseModelId'),
|
|
|
+ releaseDate: readModelAnnotation(metadata, 'releaseDate'),
|
|
|
+ parameterCount: readModelAnnotation(metadata, 'parameterCount'),
|
|
|
+ contextWindow: parseIntString(readModelAnnotation(metadata, 'contextWindow')),
|
|
|
+ inputs: parseModelModalities(readModelAnnotation(metadata, 'inputs')),
|
|
|
+ outputs: parseModelModalities(readModelAnnotation(metadata, 'outputs')),
|
|
|
+ embeddingDimensions: parseIntString(readModelAnnotation(metadata, 'embeddingDimensions')),
|
|
|
+ maxInputTokens: parseIntString(readModelAnnotation(metadata, 'maxInputTokens')),
|
|
|
+ languages: parseCsv(readModelAnnotation(metadata, 'languages')),
|
|
|
+ },
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const toExternalModelCR = (model: ExternalModel): ExternalModelCR => {
|
|
|
+ return {
|
|
|
+ apiVersion: 'locostack.com/v1alpha1',
|
|
|
+ kind: 'ExternalModel',
|
|
|
+ metadata: {
|
|
|
+ name: model.id,
|
|
|
+ annotations: modelAnnotations(model),
|
|
|
+ labels: modelLabels(model),
|
|
|
+ },
|
|
|
+ spec: {
|
|
|
+ ...(model.stack ? { stackRef: { name: model.stack } } : {}),
|
|
|
+ category: model.category,
|
|
|
+ modelName: model.name,
|
|
|
+ provider: model.provider,
|
|
|
+ providerModel: model.providerModel,
|
|
|
+ apiBase: model.apiBase,
|
|
|
+ apiVersion: model.apiVersion,
|
|
|
+ auth: toAuth(model.auth),
|
|
|
+ extraParams: model.extraParams,
|
|
|
+ ...(model.defaultInferenceParams
|
|
|
+ ? {
|
|
|
+ defaultInferenceParams: {
|
|
|
+ ...model.defaultInferenceParams,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ : {}),
|
|
|
+ },
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const fromExternalModelCR = (cr: ExternalModelCR): ExternalModel => {
|
|
|
+ const spec = cr.spec;
|
|
|
+ const metadata = cr.metadata;
|
|
|
+ const id = metadata.name;
|
|
|
+ if (id === undefined) {
|
|
|
+ throw new Error('metadata.name is required');
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...parseModelBase(cr),
|
|
|
+ mode: 'external',
|
|
|
+ provider: spec.provider,
|
|
|
+ providerModel: spec.providerModel,
|
|
|
+ apiBase: spec.apiBase,
|
|
|
+ apiVersion: spec.apiVersion,
|
|
|
+ auth: fromAuth(spec.auth),
|
|
|
+ extraParams: spec.extraParams,
|
|
|
+ defaultInferenceParams: spec.defaultInferenceParams,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const toManagedModelCR = (model: ManagedModel): ManagedModelCR => {
|
|
|
+ return {
|
|
|
+ apiVersion: 'locostack.com/v1alpha1',
|
|
|
+ kind: 'ManagedModel',
|
|
|
+ metadata: {
|
|
|
+ name: model.id,
|
|
|
+ annotations: modelAnnotations(model),
|
|
|
+ labels: modelLabels(model),
|
|
|
+ },
|
|
|
+ spec: {
|
|
|
+ ...(model.stack ? { stackRef: { name: model.stack } } : {}),
|
|
|
+ category: model.category,
|
|
|
+ modelName: model.name,
|
|
|
+ weights: {
|
|
|
+ ...(model.huggingFace
|
|
|
+ ? {
|
|
|
+ huggingFace: {
|
|
|
+ endpoint: model.huggingFace?.endpoint,
|
|
|
+ repo: model.huggingFace?.repo || '',
|
|
|
+ revision: model.huggingFace?.revision || 'main',
|
|
|
+ fileName: model.huggingFace?.fileName || '',
|
|
|
+ },
|
|
|
+ }
|
|
|
+ : {}),
|
|
|
+ },
|
|
|
+ runtimeInferenceParameters: {
|
|
|
+ ...(model.runtimeInferenceParameters?.chatTemplate
|
|
|
+ ? { chatTemplate: model.runtimeInferenceParameters.chatTemplate }
|
|
|
+ : {}),
|
|
|
+ ...(model.runtimeInferenceParameters?.contextWindow !== undefined
|
|
|
+ ? { contextWindow: model.runtimeInferenceParameters.contextWindow }
|
|
|
+ : {}),
|
|
|
+ ...(model.runtimeInferenceParameters?.temperature !== undefined
|
|
|
+ ? { temperature: model.runtimeInferenceParameters.temperature }
|
|
|
+ : {}),
|
|
|
+ ...(model.runtimeInferenceParameters?.topP !== undefined
|
|
|
+ ? { topP: model.runtimeInferenceParameters.topP }
|
|
|
+ : {}),
|
|
|
+ ...(model.runtimeInferenceParameters?.topK !== undefined
|
|
|
+ ? { topK: model.runtimeInferenceParameters.topK }
|
|
|
+ : {}),
|
|
|
+ },
|
|
|
+ template: toTemplate(model.runtime),
|
|
|
+ },
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const fromManagedModelCR = (cr: ManagedModelCR): ManagedModel => {
|
|
|
+ const spec = cr.spec;
|
|
|
+ const metadata = cr.metadata;
|
|
|
+ const id = metadata.name;
|
|
|
+ if (id === undefined) {
|
|
|
+ throw new Error('metadata.name is required');
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ ...parseModelBase(cr),
|
|
|
+ mode: 'managed',
|
|
|
+ huggingFace: spec.weights.huggingFace
|
|
|
+ ? {
|
|
|
+ endpoint: spec.weights.huggingFace.endpoint,
|
|
|
+ repo: spec.weights.huggingFace.repo,
|
|
|
+ revision: spec.weights.huggingFace.revision,
|
|
|
+ fileName: spec.weights.huggingFace.fileName,
|
|
|
+ }
|
|
|
+ : undefined,
|
|
|
+ runtime: fromTemplate(spec.template),
|
|
|
+ runtimeInferenceParameters: spec.runtimeInferenceParameters,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const fromModelCR = (cr: ExternalModelCR | ManagedModelCR): Model => {
|
|
|
+ if (cr.kind === 'ExternalModel') {
|
|
|
+ return fromExternalModelCR(cr as ExternalModelCR);
|
|
|
+ } else if (cr.kind === 'ManagedModel') {
|
|
|
+ return fromManagedModelCR(cr as ManagedModelCR);
|
|
|
+ } else {
|
|
|
+ throw new Error(`Unknown model CR kind: ${cr.kind}`);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export type { ExternalModelCR, ManagedModelCR };
|
|
|
+export {
|
|
|
+ fromExternalModelCR,
|
|
|
+ fromManagedModelCR,
|
|
|
+ fromModelCR,
|
|
|
+ toExternalModelCR,
|
|
|
+ toManagedModelCR,
|
|
|
+};
|