gadget/docs/archive/services/ai.ts

308 lines
7.6 KiB
TypeScript

// src/services/ai.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
import {
AiProvider,
type ApiType,
type IAiModelSettings,
} from "../models/ai-provider.js";
import { User } from "../models/user.js";
import { OllamaAiClient } from "../lib/ai-clients/ollama-client.js";
import { OpenAiClient } from "../lib/ai-clients/openai-client.js";
import type { AiClient, ChatOptions } from "../lib/ai-client.js";
import { DtpService } from "../lib/service.js";
interface ProviderData {
apiType: ApiType;
baseUrl: string;
apiKey: string;
}
class AiService extends DtpService {
get name(): string {
return "AiService";
}
get slug(): string {
return "ai";
}
constructor() {
super();
}
async start(): Promise<void> {
this.log.info("service started");
}
async stop(): Promise<void> {
this.log.info("service stopped");
}
getClient(provider: ProviderData): AiClient {
if (provider.apiType === "ollama") {
return new OllamaAiClient(provider.baseUrl, provider.apiKey);
}
return new OpenAiClient(provider.baseUrl, provider.apiKey);
}
async getAgentClient(userId: string): Promise<AiClient> {
const user = await User.findById(userId)
.select("+connections.ai.agentProviderId +connections.ai.agentModel")
.lean();
if (!user) {
throw new Error("User not found");
}
const aiConfig = user.connections?.ai;
if (!aiConfig?.agentProviderId) {
throw new Error("No agent provider configured");
}
const provider = await AiProvider.findById(aiConfig.agentProviderId)
.select("+apiKey")
.lean();
if (!provider) {
throw new Error("Agent provider not found");
}
return this.getClient({
apiType: provider.apiType,
baseUrl: provider.baseUrl,
apiKey: provider.apiKey,
});
}
async getAgentProviderInfo(
userId: string,
): Promise<{ providerId: string; model: string } | null> {
const user = await User.findById(userId)
.select("+connections.ai.agentProviderId +connections.ai.agentModel")
.lean();
if (!user) {
return null;
}
const aiConfig = user.connections?.ai;
if (!aiConfig?.agentProviderId || !aiConfig?.agentModel) {
return null;
}
return {
providerId: aiConfig.agentProviderId.toString(),
model: aiConfig.agentModel,
};
}
async getVectorClient(userId: string): Promise<AiClient> {
const user = await User.findById(userId)
.select("+connections.ai.vectorProviderId +connections.ai.vectorModel")
.lean();
if (!user) {
throw new Error("User not found");
}
const aiConfig = user.connections?.ai;
if (!aiConfig?.vectorProviderId) {
throw new Error("No vector provider configured");
}
const provider = await AiProvider.findById(aiConfig.vectorProviderId)
.select("+apiKey")
.lean();
if (!provider) {
throw new Error("Vector provider not found");
}
return this.getClient({
apiType: provider.apiType,
baseUrl: provider.baseUrl,
apiKey: provider.apiKey,
});
}
async getUtilityClient(userId: string): Promise<AiClient> {
const user = await User.findById(userId)
.select("+connections.ai.utilityProviderId +connections.ai.utilityModel")
.lean();
if (!user) {
throw new Error("User not found");
}
const aiConfig = user.connections?.ai;
if (!aiConfig?.utilityProviderId) {
throw new Error("No utility provider configured");
}
const provider = await AiProvider.findById(aiConfig.utilityProviderId)
.select("+apiKey")
.lean();
if (!provider) {
throw new Error("Utility provider not found");
}
return this.getClient({
apiType: provider.apiType,
baseUrl: provider.baseUrl,
apiKey: provider.apiKey,
});
}
async getAgentModel(userId: string): Promise<string> {
const user = await User.findById(userId)
.select("+connections.ai.agentModel")
.lean();
if (!user) {
throw new Error("User not found");
}
return user.connections?.ai?.agentModel || "";
}
async getVectorModel(userId: string): Promise<string> {
const user = await User.findById(userId)
.select("+connections.ai.vectorModel")
.lean();
if (!user) {
throw new Error("User not found");
}
return user.connections?.ai?.vectorModel || "";
}
async getUtilityModel(userId: string): Promise<string> {
const user = await User.findById(userId)
.select("+connections.ai.utilityModel")
.lean();
if (!user) {
throw new Error("User not found");
}
return user.connections?.ai?.utilityModel || "";
}
async getModelSettings(
userId: string,
providerId: string,
modelId: string,
): Promise<IAiModelSettings | null> {
const provider = await AiProvider.findOne({
_id: providerId,
user: userId,
}).lean();
if (!provider) {
return null;
}
const model = provider.models.find((m) => m.id === modelId);
return model?.settings ?? null;
}
async getModelChatOptions(
userId: string,
providerId: string,
modelId: string,
): Promise<ChatOptions> {
const provider = await AiProvider.findOne({
_id: providerId,
user: userId,
}).lean();
if (!provider) {
return {};
}
const model = provider.models.find((m) => m.id === modelId);
const settings = model?.settings;
if (!settings) {
const defaults = this.getDefaultOptions(
provider.apiType,
model?.contextWindow,
);
return defaults;
}
return {
temperature: settings.temperature,
topP: settings.topP,
topK: settings.topK,
numCtx: settings.numCtx,
};
}
private getDefaultOptions(
apiType: ApiType,
contextWindow?: number,
): ChatOptions {
if (apiType === "ollama") {
return {
temperature: 0.8,
topP: 0.9,
topK: 40,
numCtx: contextWindow || 2048,
};
}
return {
temperature: 0.7,
topP: 1.0,
};
}
async isConfigured(userId: string): Promise<boolean> {
const user = await User.findById(userId)
.select(
"+connections.ai.agentProviderId +connections.ai.vectorProviderId +connections.ai.utilityProviderId",
)
.lean();
if (!user) {
return false;
}
const aiConfig = user.connections?.ai;
return !!(
aiConfig?.agentProviderId &&
aiConfig?.vectorProviderId &&
aiConfig?.utilityProviderId
);
}
async getModelDisplay(
userId: string,
slot: "agent" | "vector" | "util",
): Promise<string> {
const user = await User.findById(userId)
.select(
"+connections.ai.agentModel +connections.ai.agentProviderId +connections.ai.vectorModel +connections.ai.vectorProviderId +connections.ai.utilityModel +connections.ai.utilityProviderId",
)
.lean();
if (!user) return "N/A";
const aiConfig = user.connections?.ai;
let model = "";
let providerId: string | null = null;
if (slot === "agent") {
model = aiConfig?.agentModel || "";
providerId = aiConfig?.agentProviderId?.toString() || null;
} else if (slot === "vector") {
model = aiConfig?.vectorModel || "";
providerId = aiConfig?.vectorProviderId?.toString() || null;
} else {
model = aiConfig?.utilityModel || "";
providerId = aiConfig?.utilityProviderId?.toString() || null;
}
if (!model || !providerId) return "N/A";
const provider = await AiProvider.findById(providerId)
.select("name")
.lean();
const providerName = provider?.name || "Unknown";
return `${model} (${providerName})`;
}
}
export default new AiService();