presenton/servers/nextjs/utils/providerUtils.ts
2025-07-23 00:00:06 +05:45

284 lines
No EOL
7.3 KiB
TypeScript

import { toast } from "sonner";
export interface OllamaModel {
label: string;
value: string;
description: string;
size: string;
icon: string;
}
export interface DownloadingModel {
name: string;
size: number | null;
downloaded: number | null;
status: string;
done: boolean;
}
export interface LLMConfig {
LLM?: string;
OPENAI_API_KEY?: string;
GOOGLE_API_KEY?: string;
OLLAMA_URL?: string;
OLLAMA_MODEL?: string;
CUSTOM_LLM_URL?: string;
CUSTOM_LLM_API_KEY?: string;
CUSTOM_MODEL?: string;
PEXELS_API_KEY?: string;
PIXABAY_API_KEY?: string;
IMAGE_PROVIDER?: string;
USE_CUSTOM_URL?: boolean;
}
export interface OllamaModelsResult {
models: OllamaModel[];
updatedConfig?: LLMConfig;
}
/**
* Updates LLM configuration based on field changes
*/
export const updateLLMConfig = (
currentConfig: LLMConfig,
field: string,
value: string
): LLMConfig => {
const fieldMappings: Record<string, keyof LLMConfig> = {
openai_api_key: "OPENAI_API_KEY",
google_api_key: "GOOGLE_API_KEY",
ollama_url: "OLLAMA_URL",
ollama_model: "OLLAMA_MODEL",
custom_llm_url: "CUSTOM_LLM_URL",
custom_llm_api_key: "CUSTOM_LLM_API_KEY",
custom_model: "CUSTOM_MODEL",
pexels_api_key: "PEXELS_API_KEY",
pixabay_api_key: "PIXABAY_API_KEY",
image_provider: "IMAGE_PROVIDER",
};
const configKey = fieldMappings[field];
if (configKey) {
return { ...currentConfig, [configKey]: value };
}
return currentConfig;
};
/**
* Changes the provider and sets appropriate defaults
*/
export const changeProvider = (
currentConfig: LLMConfig,
provider: string
): LLMConfig => {
const newConfig = { ...currentConfig, LLM: provider };
// Auto Select appropriate image provider based on the text models
if (provider === "openai") {
newConfig.IMAGE_PROVIDER = "dall-e-3";
} else if (provider === "google") {
newConfig.IMAGE_PROVIDER = "gemini_flash";
} else {
newConfig.IMAGE_PROVIDER = "pexels"; // default for ollama and custom
}
return newConfig;
};
/**
* Fetches supported Ollama models
*/
export const fetchOllamaModels = async (): Promise<OllamaModel[]> => {
try {
const response = await fetch("/api/v1/ppt/ollama/models/supported");
const models = await response.json();
return models || [];
} catch (error) {
console.error("Error fetching ollama models:", error);
return []; // Ensure we always return an empty array on error
}
};
/**
* Fetches Ollama models and validates current selection
* Returns models and updated config if needed
*/
export const fetchOllamaModelsWithConfig = async (
config: LLMConfig
): Promise<OllamaModelsResult> => {
try {
const models = await fetchOllamaModels();
// Check if currently selected model is still available
let updatedConfig: LLMConfig | undefined;
if (config.OLLAMA_MODEL && models && models.length > 0) {
const isModelAvailable = models.some(
(model: OllamaModel) => model.value === config.OLLAMA_MODEL
);
if (!isModelAvailable) {
updatedConfig = { ...config, OLLAMA_MODEL: "" };
}
}
return {
models,
updatedConfig
};
} catch (error) {
console.error("Error fetching ollama models:", error);
return {
models: [],
updatedConfig: { ...config, OLLAMA_MODEL: "" }
};
}
};
export const checkIfSelectedOllamaModelIsPulled = async (ollamaModel: string) => {
try {
const response = await fetch('/api/v1/ppt/ollama/models/available');
const models = await response.json();
const pulledModels = models.map((model: any) => model.name);
return pulledModels.includes(ollamaModel);
} catch (error) {
console.error('Error checking if selected Ollama model is pulled:', error);
return false;
}
}
/**
* Fetches available custom models
*/
export const fetchCustomModels = async (
url: string,
apiKey: string
): Promise<string[]> => {
try {
const response = await fetch("/api/v1/ppt/custom_llm/models/available", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
url: url || "",
api_key: apiKey || "",
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
toast.info("Could not fetch custom models");
console.error("Error fetching custom models:", error);
throw error;
}
};
/**
* Resets downloading model state
*/
export const resetDownloadingModel = (): DownloadingModel => ({
name: "",
size: null,
downloaded: null,
status: "",
done: false,
});
/**
* Pulls Ollama model with progress tracking
* Returns a promise that resolves with the final downloading model state
*/
export const pullOllamaModel = async (
model: string,
onProgress?: (model: DownloadingModel) => void
): Promise<DownloadingModel> => {
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const response = await fetch(
`/api/v1/ppt/ollama/model/pull?model=${model}`
);
if (response.status === 200) {
const data = await response.json();
if (data.done && data.status !== "error") {
clearInterval(interval);
onProgress?.(data);
resolve(data);
} else if (data.status === "error") {
clearInterval(interval);
const resetData = resetDownloadingModel();
onProgress?.(resetData);
reject(new Error("Error occurred while pulling model"));
} else {
onProgress?.(data);
}
} else {
clearInterval(interval);
const resetData = resetDownloadingModel();
onProgress?.(resetData);
if (response.status === 403) {
reject(new Error("Request to Ollama Not Authorized"));
}
reject(new Error("Error occurred while pulling model"));
}
} catch (error) {
clearInterval(interval);
const resetData = resetDownloadingModel();
onProgress?.(resetData);
reject(error);
}
}, 1000);
});
};
/**
* Sets Ollama configuration based on custom URL preference
*/
export const setOllamaConfig = (
currentConfig: LLMConfig,
useCustomUrl: boolean
): LLMConfig => {
let customUrl = "http://localhost:11434";
if (!useCustomUrl) {
return {
...currentConfig,
OLLAMA_URL: customUrl,
USE_CUSTOM_URL: false,
};
} else {
return { ...currentConfig, USE_CUSTOM_URL: true, OLLAMA_URL: customUrl };
}
};
/**
* Handles saving configuration with error handling
*/
export const handleSaveConfiguration = async (
llmConfig: LLMConfig,
handleSaveLLMConfig: (config: LLMConfig) => Promise<void>,
pullOllamaModels?: () => Promise<void>
): Promise<void> => {
try {
await handleSaveLLMConfig(llmConfig);
if (llmConfig.LLM === "ollama" && pullOllamaModels) {
await pullOllamaModels();
}
toast.success("Configuration saved successfully");
} catch (error) {
console.error("Error:", error);
toast.error(
error instanceof Error
? error.message
: "Failed to save configuration",
{
description: "Failed to save configuration",
}
);
throw error;
}
};