HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/agentcloud/webapp/src/controllers/model.ts
'use strict';

import { dynamicResponse } from '@dr';
import { removeAgentsModel } from 'db/agent';
import { getCredentialById, getCredentialsById, getCredentialsByTeam } from 'db/credential';
import { addModel, deleteModelById, getModelById, getModelsByTeam,updateModel } from 'db/model';
import dotenv from 'dotenv';
import toObjectId from 'misc/toobjectid';
import { ObjectId } from 'mongodb';
import { CredentialType } from 'struct/credential';
import { ModelEmbeddingLength, ModelList } from 'struct/model';
import { chainValidations, PARENT_OBJECT_FIELD_NAME, validateField } from 'utils/validationUtils';

import { addCredential, deleteCredentialById } from '../db/credential';
dotenv.config({ path: '.env' });

export async function modelsData(req, res, _next) {
	const [models, credentials] = await Promise.all([
		getModelsByTeam(req.params.resourceSlug),
		getCredentialsByTeam(req.params.resourceSlug)
	]);
	return {
		csrf: req.csrfToken(),
		models,
		credentials,
	};
}

export async function modelData(req, res, _next) {
	const [model, credentials] = await Promise.all([
		getModelById(req.params.resourceSlug, req.params.modelId),
		getCredentialsByTeam(req.params.resourceSlug),
	]);
	return {
		csrf: req.csrfToken(),
		model,
		credentials,
	};
}

/**
* GET /[resourceSlug]/models
* models page html
*/
export async function modelsPage(app, req, res, next) {
	const data = await modelsData(req, res, next);
	res.locals.data = { ...data, account: res.locals.account };
	return app.render(req, res, `/${req.params.resourceSlug}/models`);
}

/**
* GET /[resourceSlug]/models.json
* models json data
*/
export async function modelsJson(req, res, next) {
	const data = await modelsData(req, res, next);
	return res.json({ ...data, account: res.locals.account });
}

export async function modelJson(req, res, next) {
	const data = await modelData(req, res, next);
	return res.json({ ...data, account: res.locals.account });
}

/**
* GET /[resourceSlug]/model/add
* models add page html
*/
export async function modelAddPage(app, req, res, next) {
	const data = await modelsData(req, res, next);
	res.locals.data = { ...data, account: res.locals.account };
	return app.render(req, res, `/${req.params.resourceSlug}/model/add`);
}

export async function modelAddApi(req, res, next) {

	let { name, model, credentialId, config, type }  = req.body;

	let validationError = chainValidations(req.body, [
		{ field: 'name', validation: { notEmpty: true }},
		// { field: 'credentialId', validation: { notEmpty: true, hasLength: 24 }},
		{ field: 'model', validation: { notEmpty: true }},
	], { name: 'Name', credentialId: 'Credential', model: 'Model'});
	if (validationError) {	
		return dynamicResponse(req, res, 400, { error: validationError });
	}

	let credential;
	if (credentialId && credentialId.length > 0) {
		// Validate model for credential is valid
		validationError = await valdiateCredentialModel(req.params.resourceSlug, credentialId, model);
		if (validationError) {
			return dynamicResponse(req, res, 400, { error: validationError });
		}
		// Check for credential
		credential = await getCredentialById(req.params.resourceSlug, credentialId);
		if (!credential) {
			return dynamicResponse(req, res, 400, { error: 'Invalid credential ID' });
		}
	}

	// Insert model to db
	const addedModel = await addModel({
		orgId: res.locals.matchingOrg.id,
		teamId: toObjectId(req.params.resourceSlug),
		name,
		model,
		embeddingLength: ModelEmbeddingLength[model] || 0,
		modelType: ModelEmbeddingLength[model] ? 'embedding' : 'llm',
		type: credential?.type || type || CredentialType.FASTEMBED,
		config: config || {}, //TODO: validation
		...(credentialId ? { credentialId: toObjectId(credentialId) } : {}),
	});

	return dynamicResponse(req, res, 302, { _id: addedModel.insertedId, redirect: `/${req.params.resourceSlug}/models` });

}

export async function editModelApi(req, res, next) {

	let { name, model, credentialId, config, type }  = req.body;

	let validationError = chainValidations(req.body, [
		{ field: 'name', validation: { notEmpty: true }},
		// { field: 'credentialId', validation: { notEmpty: true, hasLength: 24 }},
		{ field: 'model', validation: { notEmpty: true }},
	], { name: 'Name', credentialId: 'Credential', model: 'Model'});
	if (validationError) {	
		return dynamicResponse(req, res, 400, { error: validationError });
	}

	const update = {
		name,
		model,
		config,
		embeddingLength: ModelEmbeddingLength[model] || 0,
		modelType: ModelEmbeddingLength[model] ? 'embedding' : 'llm',
	};

	let credential;
	if (credentialId && credentialId.length > 0) {
		// Validate model for credential is valid
		validationError = await valdiateCredentialModel(req.params.resourceSlug, credentialId, model);
		if (validationError) {
			return dynamicResponse(req, res, 400, { error: validationError });
		}
		// Check for credential
		credential = await getCredentialById(req.params.resourceSlug, credentialId);
		if (!credential) {
			return dynamicResponse(req, res, 400, { error: 'Invalid credential ID' });
		}
	}
	update['credentialId'] = credentialId ? toObjectId(credentialId) : null;
	update['type'] = credential?.type || type || CredentialType.FASTEMBED;

	// Insert model to db
	const updatedModel = await updateModel(req.params.resourceSlug, req.params.modelId, update);

	return dynamicResponse(req, res, 302, { });

}

/**
 * @api {delete} /forms/model/[modelId] Delete a model
 * @apiName delete
 * @apiGroup Model
 *
 * @apiParam {String} modelId Model id
 */
export async function deleteModelApi(req, res, next) {

	const { modelId }  = req.body;

	if (!modelId || typeof modelId !== 'string' || modelId.length !== 24) {
		return dynamicResponse(req, res, 400, { error: 'Invalid inputs' });
	}

	const model = await getModelById(req.params.resourceSlug, modelId);
	if (!model) {
		return dynamicResponse(req, res, 400, { error: 'Invalid inputs' });
	}

	Promise.all([
		removeAgentsModel(req.params.resourceSlug, modelId),
		deleteModelById(req.params.resourceSlug, modelId),
		model?.type === CredentialType.FASTEMBED ? deleteCredentialById(req.params.resourceSlug, model.credentialId) : void 0, //Delete dumym cred if this is a fastembed model
	]);

	return dynamicResponse(req, res, 302, { /*redirect: `/${req.params.resourceSlug}/credentials`*/ });

}

async function valdiateCredentialModel(teamId, credentialId, model) {
	const credential = await getCredentialById(teamId, credentialId);
	if (credential) {
		const allowedModels = ModelList[credential.type];
		return validateField(model, PARENT_OBJECT_FIELD_NAME, { inSet: allowedModels ? new Set(allowedModels) : undefined /* allows invalid types */, customError: `Model ${model} is not valid for provided credential` }, {});
	} else {
		return 'Invalid credential';
	}
}