fix(nextjs): changes ollama and custom llm endpoints, fix(fastapi): fixes broken image search due to ImageAsset

This commit is contained in:
sauravniraula 2025-07-19 13:26:29 +05:45
parent 41a17da47d
commit 6ae502fc9e
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
8 changed files with 210 additions and 208 deletions

View file

@ -1,6 +1,8 @@
from fastapi import APIRouter
from models.image_prompt import ImagePrompt
from models.sql.image_asset import ImageAsset
from services.database import get_sql_session
from services.image_generation_service import ImageGenerationService
from utils.asset_directory_utils import get_images_directory
@ -13,4 +15,12 @@ async def generate_image(prompt: str):
image_prompt = ImagePrompt(prompt=prompt)
image_generation_service = ImageGenerationService(images_directory)
return await image_generation_service.generate_image(image_prompt)
image = await image_generation_service.generate_image(image_prompt)
if not isinstance(image, ImageAsset):
return image
with get_sql_session() as sql_session:
sql_session.add(image)
sql_session.commit()
return image.path

View file

@ -22,7 +22,7 @@ async def get_available_models():
return await list_pulled_ollama_models()
@OLLAMA_ROUTER.get("/models/pull", response_model=OllamaModelStatus)
@OLLAMA_ROUTER.get("/model/pull", response_model=OllamaModelStatus)
async def pull_model(model: str, background_tasks: BackgroundTasks):
if model not in SUPPORTED_OLLAMA_MODELS:

View file

@ -8,7 +8,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="❌ Graphs not supported.",
size="4.7GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3:70b": OllamaModelMetadata(
label="Llama 3:70b",
@ -16,7 +16,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="40GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.1:8b": OllamaModelMetadata(
label="Llama 3.1:8b",
@ -24,7 +24,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="❌ Graphs not supported.",
size="4.9GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.1:70b": OllamaModelMetadata(
label="Llama 3.1:70b",
@ -32,7 +32,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="43GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.1:405b": OllamaModelMetadata(
label="Llama 3.1:405b",
@ -40,7 +40,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="243GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.2:1b": OllamaModelMetadata(
label="Llama 3.2:1b",
@ -48,7 +48,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="❌ Graphs not supported.",
size="1.3GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.2:3b": OllamaModelMetadata(
label="Llama 3.2:3b",
@ -56,7 +56,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="❌ Graphs not supported.",
size="2GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama3.3:70b": OllamaModelMetadata(
label="Llama 3.3:70b",
@ -64,7 +64,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="43GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama4:16x17b": OllamaModelMetadata(
label="Llama 4:16x17b",
@ -72,7 +72,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="67GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
"llama4:128x17b": OllamaModelMetadata(
label="Llama 4:128x17b",
@ -80,7 +80,7 @@ SUPPORTED_OLLAMA_MODELS = {
description="✅ Graphs supported.",
size="245GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/meta.png",
icon="/static/icons/meta.png",
),
}
@ -91,7 +91,7 @@ SUPPORTED_GEMMA_MODELS = {
description="❌ Graphs not supported.",
size="815MB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/gemma.png",
icon="/static/icons/gemma.png",
),
"gemma3:4b": OllamaModelMetadata(
label="Gemma 3:4b",
@ -99,7 +99,7 @@ SUPPORTED_GEMMA_MODELS = {
description="❌ Graphs not supported.",
size="3.3GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/gemma.png",
icon="/static/icons/gemma.png",
),
"gemma3:12b": OllamaModelMetadata(
label="Gemma 3:12b",
@ -107,7 +107,7 @@ SUPPORTED_GEMMA_MODELS = {
description="❌ Graphs not supported.",
size="8.1GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/gemma.png",
icon="/static/icons/gemma.png",
),
"gemma3:27b": OllamaModelMetadata(
label="Gemma 3:27b",
@ -115,7 +115,7 @@ SUPPORTED_GEMMA_MODELS = {
description="✅ Graphs supported.",
size="17GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/gemma.png",
icon="/static/icons/gemma.png",
),
}
@ -126,7 +126,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="❌ Graphs not supported.",
size="1.1GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:7b": OllamaModelMetadata(
label="DeepSeek R1:7b",
@ -134,7 +134,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="❌ Graphs not supported.",
size="4.7GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:8b": OllamaModelMetadata(
label="DeepSeek R1:8b",
@ -142,7 +142,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="❌ Graphs not supported.",
size="5.2GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:14b": OllamaModelMetadata(
label="DeepSeek R1:14b",
@ -150,7 +150,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="❌ Graphs not supported.",
size="9GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:32b": OllamaModelMetadata(
label="DeepSeek R1:32b",
@ -158,7 +158,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="✅ Graphs supported.",
size="20GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:70b": OllamaModelMetadata(
label="DeepSeek R1:70b",
@ -166,7 +166,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="✅ Graphs supported.",
size="43GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
"deepseek-r1:671b": OllamaModelMetadata(
label="DeepSeek R1:671b",
@ -174,7 +174,7 @@ SUPPORTED_DEEPSEEK_MODELS = {
description="✅ Graphs supported.",
size="404GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/deepseek.png",
icon="/static/icons/deepseek.png",
),
}
@ -185,7 +185,7 @@ SUPPORTED_QWEN_MODELS = {
description="❌ Graphs not supported.",
size="523MB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:1.7b": OllamaModelMetadata(
label="Qwen 3:1.7b",
@ -193,7 +193,7 @@ SUPPORTED_QWEN_MODELS = {
description="❌ Graphs not supported.",
size="1.4GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:4b": OllamaModelMetadata(
label="Qwen 3:4b",
@ -201,7 +201,7 @@ SUPPORTED_QWEN_MODELS = {
description="❌ Graphs not supported.",
size="2.6GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:8b": OllamaModelMetadata(
label="Qwen 3:8b",
@ -209,7 +209,7 @@ SUPPORTED_QWEN_MODELS = {
description="❌ Graphs not supported.",
size="5.2GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:14b": OllamaModelMetadata(
label="Qwen 3:14b",
@ -217,7 +217,7 @@ SUPPORTED_QWEN_MODELS = {
description="❌ Graphs not supported.",
size="9.3GB",
supports_graph=False,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:30b": OllamaModelMetadata(
label="Qwen 3:30b",
@ -225,7 +225,7 @@ SUPPORTED_QWEN_MODELS = {
description="✅ Graphs supported.",
size="19GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:32b": OllamaModelMetadata(
label="Qwen 3:32b",
@ -233,7 +233,7 @@ SUPPORTED_QWEN_MODELS = {
description="✅ Graphs supported.",
size="20GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
"qwen3:235b": OllamaModelMetadata(
label="Qwen 3:235b",
@ -241,7 +241,7 @@ SUPPORTED_QWEN_MODELS = {
description="✅ Graphs supported.",
size="142GB",
supports_graph=True,
icon="/static/servers/fastapi/assets/icons/qwen.png",
icon="/static/icons/qwen.png",
),
}

View file

@ -9,18 +9,15 @@ from models.sql.image_asset import ImageAsset
from utils.download_helpers import download_file
from utils.get_env import get_pexels_api_key_env
from utils.get_env import get_pixabay_api_key_env
from utils.llm_provider import (
get_llm_client,
is_google_selected,
is_openai_selected,
)
from utils.llm_provider import get_llm_client
from utils.image_provider import (
is_pixels_selected,
is_pixabay_selected,
is_gemini_flash_selected,
is_dalle3_selected
is_dalle3_selected,
)
class ImageGenerationService:
def __init__(self, output_directory: str):
@ -53,14 +50,18 @@ class ImageGenerationService:
print("No image generation function found. Using placeholder image.")
return "/static/images/placeholder.jpg"
image_prompt = prompt.get_image_prompt(with_theme=not self.is_stock_provider_selected())
image_prompt = prompt.get_image_prompt(
with_theme=not self.is_stock_provider_selected()
)
print(f"Request - Generating Image for {image_prompt}")
try:
if self.is_stock_provider_selected():
image_path = await self.image_gen_func(image_prompt)
else:
image_path = await self.image_gen_func(image_prompt, self.output_directory)
image_path = await self.image_gen_func(
image_prompt, self.output_directory
)
if image_path:
if image_path.startswith("http"):
return image_path
@ -118,11 +119,11 @@ class ImageGenerationService:
data = await response.json()
image_url = data["photos"][0]["src"]["large"]
return image_url
async def get_image_from_pixabay(self, prompt: str) -> str:
async with aiohttp.ClientSession() as session:
response = await session.get(
f"https://pixabay.com/api/?key={get_pixabay_api_key_env()}&q={prompt}&image_type=photo&per_page=3"
f"https://pixabay.com/api/?key={get_pixabay_api_key_env()}&q={prompt}&image_type=photo&per_page=1"
)
data = await response.json()
image_url = data["hits"][0]["largeImageURL"]

View file

@ -1,4 +1,3 @@
import os
from enums.image_provider import ImageProvider
from utils.get_env import (
get_google_api_key_env,

View file

@ -178,13 +178,13 @@ const SettingsPage = () => {
const fetchOllamaModelsWithConfig = async (config: any) => {
try {
const response = await fetch("/api/v1/ppt/ollama/list-supported-models");
const data = await response.json();
setOllamaModels(data.models);
const response = await fetch("/api/v1/ppt/ollama/models/supported");
const models = await response.json();
setOllamaModels(models);
// Check if currently selected model is still available
if (config.OLLAMA_MODEL && data.models.length > 0) {
const isModelAvailable = data.models.some(
if (config.OLLAMA_MODEL && models.length > 0) {
const isModelAvailable = models.some(
(model: any) => model.value === config.OLLAMA_MODEL
);
if (!isModelAvailable) {
@ -220,7 +220,7 @@ const SettingsPage = () => {
const interval = setInterval(async () => {
try {
const response = await fetch(
`/api/v1/ppt/ollama/pull-model?name=${llmConfig.OLLAMA_MODEL}`
`/api/v1/ppt/ollama/model/pull?model=${llmConfig.OLLAMA_MODEL}`
);
if (response.status === 200) {
const data = await response.json();
@ -259,7 +259,7 @@ const SettingsPage = () => {
const fetchCustomModels = async () => {
try {
setCustomModelsLoading(true);
const response = await fetch("/api/v1/ppt/models/list/custom", {
const response = await fetch("/api/v1/ppt/custom_llm/models/available", {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -384,24 +384,22 @@ const SettingsPage = () => {
<button
key={provider}
onClick={() => changeProvider(provider)}
className={`relative p-4 rounded-lg border-2 transition-all duration-200 ${
llmConfig.LLM === provider
className={`relative p-4 rounded-lg border-2 transition-all duration-200 ${llmConfig.LLM === provider
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-blue-200 hover:bg-gray-50"
}`}
}`}
>
<div className="flex items-center justify-center gap-3">
<span
className={`font-medium text-center ${
llmConfig.LLM === provider
className={`font-medium text-center ${llmConfig.LLM === provider
? "text-blue-700"
: "text-gray-700"
}`}
}`}
>
{provider === "openai"
? "OpenAI"
: provider.charAt(0).toUpperCase() +
provider.slice(1)}
provider.slice(1)}
</span>
</div>
</button>
@ -481,8 +479,8 @@ const SettingsPage = () => {
<span className="text-sm font-medium text-gray-900">
{llmConfig.OLLAMA_MODEL
? ollamaModels.find(
(m) => m.value === llmConfig.OLLAMA_MODEL
)?.label || llmConfig.OLLAMA_MODEL
(m) => m.value === llmConfig.OLLAMA_MODEL
)?.label || llmConfig.OLLAMA_MODEL
: "Select a model"}
</span>
{llmConfig.OLLAMA_MODEL && (
@ -686,80 +684,80 @@ const SettingsPage = () => {
{/* Model selection dropdown - show if models are available or if there's a selected model */}
{((customModelsChecked && customModels.length > 0) ||
llmConfig.CUSTOM_MODEL) && (
<div>
<div className="mb-3 p-3 bg-amber-50 border border-amber-200 rounded-lg">
<p className="text-sm text-amber-800">
<strong>Important:</strong> Only models with function
calling capabilities (tool calls) or JSON schema support
will work.
</p>
</div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Select Model
</label>
<div className="w-full">
<Popover
open={openModelSelect}
onOpenChange={setOpenModelSelect}
>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={openModelSelect}
className="w-full h-12 px-4 py-4 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors hover:border-gray-400 justify-between"
>
<span className="text-sm font-medium text-gray-900">
{llmConfig.CUSTOM_MODEL || "Select a model"}
</span>
<ChevronsUpDown className="w-4 h-4 text-gray-500" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
align="start"
style={{
width: "var(--radix-popover-trigger-width)",
}}
<div>
<div className="mb-3 p-3 bg-amber-50 border border-amber-200 rounded-lg">
<p className="text-sm text-amber-800">
<strong>Important:</strong> Only models with function
calling capabilities (tool calls) or JSON schema support
will work.
</p>
</div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Select Model
</label>
<div className="w-full">
<Popover
open={openModelSelect}
onOpenChange={setOpenModelSelect}
>
<Command>
<CommandInput placeholder="Search model..." />
<CommandList>
<CommandEmpty>No model found.</CommandEmpty>
<CommandGroup>
{customModels.map((model, index) => (
<CommandItem
key={index}
value={model}
onSelect={(value) => {
setLlmConfig({
...llmConfig,
CUSTOM_MODEL: value,
});
setOpenModelSelect(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
llmConfig.CUSTOM_MODEL === model
? "opacity-100"
: "opacity-0"
)}
/>
<span className="text-sm font-medium text-gray-900">
{model}
</span>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={openModelSelect}
className="w-full h-12 px-4 py-4 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors hover:border-gray-400 justify-between"
>
<span className="text-sm font-medium text-gray-900">
{llmConfig.CUSTOM_MODEL || "Select a model"}
</span>
<ChevronsUpDown className="w-4 h-4 text-gray-500" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
align="start"
style={{
width: "var(--radix-popover-trigger-width)",
}}
>
<Command>
<CommandInput placeholder="Search model..." />
<CommandList>
<CommandEmpty>No model found.</CommandEmpty>
<CommandGroup>
{customModels.map((model, index) => (
<CommandItem
key={index}
value={model}
onSelect={(value) => {
setLlmConfig({
...llmConfig,
CUSTOM_MODEL: value,
});
setOpenModelSelect(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
llmConfig.CUSTOM_MODEL === model
? "opacity-100"
: "opacity-0"
)}
/>
<span className="text-sm font-medium text-gray-900">
{model}
</span>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</div>
</div>
)}
)}
{/* Check for available models button - show when no models checked or no models found, and no model is selected */}
{(!customModelsChecked ||
@ -771,11 +769,10 @@ const SettingsPage = () => {
disabled={
customModelsLoading || !llmConfig.CUSTOM_LLM_URL
}
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 font-semibold ${
customModelsLoading || !llmConfig.CUSTOM_LLM_URL
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 font-semibold ${customModelsLoading || !llmConfig.CUSTOM_LLM_URL
? "bg-gray-100 border-gray-300 cursor-not-allowed text-gray-500"
: "bg-white border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-2 focus:ring-blue-500/20"
}`}
}`}
>
{customModelsLoading ? (
<div className="flex items-center justify-center gap-2">
@ -806,11 +803,10 @@ const SettingsPage = () => {
disabled={
customModelsLoading || !llmConfig.CUSTOM_LLM_URL
}
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 font-semibold ${
customModelsLoading || !llmConfig.CUSTOM_LLM_URL
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 font-semibold ${customModelsLoading || !llmConfig.CUSTOM_LLM_URL
? "bg-gray-100 border-gray-300 cursor-not-allowed text-gray-500"
: "bg-white border-gray-600 text-gray-600 hover:bg-gray-50 focus:ring-2 focus:ring-gray-500/20"
}`}
}`}
>
{customModelsLoading ? (
<div className="flex items-center justify-center gap-2">
@ -868,7 +864,7 @@ const SettingsPage = () => {
<span className="text-sm font-medium text-gray-900">
{llmConfig.IMAGE_PROVIDER
? IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]
?.title || llmConfig.IMAGE_PROVIDER
?.title || llmConfig.IMAGE_PROVIDER
: "Select image provider"}
</span>
</div>
@ -962,8 +958,8 @@ const SettingsPage = () => {
provider.apiKeyField === "PEXELS_API_KEY"
? llmConfig.PEXELS_API_KEY || ""
: provider.apiKeyField === "PIXABAY_API_KEY"
? llmConfig.PIXABAY_API_KEY || ""
: ""
? llmConfig.PIXABAY_API_KEY || ""
: ""
}
onChange={(e) => {
if (provider.apiKeyField === "PEXELS_API_KEY") {
@ -998,25 +994,24 @@ const SettingsPage = () => {
(llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||
(llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL)
}
className={`mt-8 w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${
isLoading ||
(llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||
(llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL)
className={`mt-8 w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${isLoading ||
(llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||
(llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL)
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}
} text-white`}
>
{isLoading ? (
<div className="flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
{(llmConfig.LLM === "ollama" &&
downloadingModel.downloaded) ||
0 > 0
0 > 0
? `Downloading Model (${(
((downloadingModel.downloaded || 0) /
(downloadingModel.size || 1)) *
100
).toFixed(0)}%)`
((downloadingModel.downloaded || 0) /
(downloadingModel.size || 1)) *
100
).toFixed(0)}%)`
: "Saving Configuration..."}
</div>
) : (llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||

View file

@ -84,9 +84,9 @@ export function StoreInitializer({ children }: { children: React.ReactNode }) {
const checkIfSelectedOllamaModelIsPulled = async (ollamaModel: string) => {
try {
const response = await fetch('/api/v1/ppt/ollama/list-pulled-models');
const data = await response.json();
const pulledModels = data.map((model: any) => model.name);
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);
@ -96,7 +96,7 @@ export function StoreInitializer({ children }: { children: React.ReactNode }) {
const checkIfSelectedCustomModelIsAvailable = async (customModel: string) => {
try {
const response = await fetch('/api/v1/ppt/models/list/custom', {
const response = await fetch('/api/v1/ppt/custom_llm/models/available', {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View file

@ -214,7 +214,8 @@ export default function Home() {
const router = useRouter();
const [openImageProviderSelect, setOpenImageProviderSelect] = useState(false);
const config = useSelector((state: RootState) => state.userConfig);
const [llmConfig, setLlmConfig] = useState({...config.llm_config,
const [llmConfig, setLlmConfig] = useState({
...config.llm_config,
IMAGE_PROVIDER: "dall-e-3",
});
const [ollamaModels, setOllamaModels] = useState<
@ -297,13 +298,13 @@ export default function Home() {
const fetchOllamaModelsWithConfig = async (config: any) => {
try {
const response = await fetch("/api/v1/ppt/ollama/list-supported-models");
const data = await response.json();
setOllamaModels(data.models || []);
const response = await fetch("/api/v1/ppt/ollama/models/supported");
const models = await response.json();
setOllamaModels(models || []);
// Check if currently selected model is still available
if (config.OLLAMA_MODEL && data.models && data.models.length > 0) {
const isModelAvailable = data.models.some(
if (config.OLLAMA_MODEL && models && models.length > 0) {
const isModelAvailable = models.some(
(model: any) => model.value === config.OLLAMA_MODEL
);
if (!isModelAvailable) {
@ -350,7 +351,7 @@ export default function Home() {
const interval = setInterval(async () => {
try {
const response = await fetch(
`/api/v1/ppt/ollama/pull-model?name=${llmConfig.OLLAMA_MODEL}`
`/api/v1/ppt/ollama/model/pull?model=${llmConfig.OLLAMA_MODEL}`
);
if (response.status === 200) {
const data = await response.json();
@ -389,7 +390,7 @@ export default function Home() {
const fetchCustomModels = async () => {
try {
setCustomModelsLoading(true);
const response = await fetch("/api/v1/ppt/models/list/custom", {
const response = await fetch("/api/v1/ppt/custom_llm/models/available", {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -505,19 +506,17 @@ export default function Home() {
<button
key={provider}
onClick={() => changeProvider(provider)}
className={`relative p-4 rounded-lg border-2 transition-all duration-200 ${
llmConfig.LLM === provider
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-blue-200 hover:bg-gray-50"
}`}
className={`relative p-4 rounded-lg border-2 transition-all duration-200 ${llmConfig.LLM === provider
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-blue-200 hover:bg-gray-50"
}`}
>
<div className="flex items-center justify-center gap-3">
<span
className={`font-medium text-center ${
llmConfig.LLM === provider
? "text-blue-700"
: "text-gray-700"
}`}
className={`font-medium text-center ${llmConfig.LLM === provider
? "text-blue-700"
: "text-gray-700"
}`}
>
{provider === "openai"
? "OpenAI"
@ -599,8 +598,8 @@ export default function Home() {
<span className="text-sm font-medium text-gray-900">
{llmConfig.OLLAMA_MODEL
? ollamaModels?.find(
(m) => m.value === llmConfig.OLLAMA_MODEL
)?.label || llmConfig.OLLAMA_MODEL
(m) => m.value === llmConfig.OLLAMA_MODEL
)?.label || llmConfig.OLLAMA_MODEL
: "Select a model"}
</span>
{llmConfig.OLLAMA_MODEL && (
@ -840,27 +839,26 @@ export default function Home() {
{/* Check for available models button - show when no models checked or no models found */}
{(!customModelsChecked ||
(customModelsChecked && customModels.length === 0)) && (
<div className="mb-4">
<button
onClick={fetchCustomModels}
disabled={customModelsLoading || !llmConfig.CUSTOM_LLM_URL}
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 ${
customModelsLoading || !llmConfig.CUSTOM_LLM_URL
<div className="mb-4">
<button
onClick={fetchCustomModels}
disabled={customModelsLoading || !llmConfig.CUSTOM_LLM_URL}
className={`w-full py-2.5 px-4 rounded-lg transition-all duration-200 border-2 ${customModelsLoading || !llmConfig.CUSTOM_LLM_URL
? "bg-gray-100 border-gray-300 cursor-not-allowed text-gray-500"
: "bg-white border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-2 focus:ring-blue-500/20"
}`}
>
{customModelsLoading ? (
<div className="flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
Checking for models...
</div>
) : (
"Check for available models"
)}
</button>
</div>
)}
}`}
>
{customModelsLoading ? (
<div className="flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
Checking for models...
</div>
) : (
"Check for available models"
)}
</button>
</div>
)}
{/* Show message if no models found */}
{customModelsChecked && customModels.length === 0 && (
@ -893,7 +891,7 @@ export default function Home() {
<span className="text-sm font-medium text-gray-900">
{llmConfig.IMAGE_PROVIDER
? IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]?.label ||
llmConfig.IMAGE_PROVIDER
llmConfig.IMAGE_PROVIDER
: "Select image provider"}
</span>
</div>
@ -986,8 +984,8 @@ export default function Home() {
provider.apiKeyField === "PEXELS_API_KEY"
? llmConfig.PEXELS_API_KEY || ""
: provider.apiKeyField === "PIXABAY_API_KEY"
? llmConfig.PIXABAY_API_KEY || ""
: ""
? llmConfig.PIXABAY_API_KEY || ""
: ""
}
onChange={(e) => {
if (provider.apiKeyField === "PEXELS_API_KEY") {
@ -1022,11 +1020,11 @@ export default function Home() {
{llmConfig.LLM === "ollama"
? llmConfig.OLLAMA_MODEL ?? "_____"
: llmConfig.LLM === "custom"
? llmConfig.CUSTOM_MODEL ?? "_____"
: PROVIDER_CONFIGS[llmConfig.LLM!].textModels[0].label}{" "}
? llmConfig.CUSTOM_MODEL ?? "_____"
: PROVIDER_CONFIGS[llmConfig.LLM!].textModels[0].label}{" "}
for text generation and{" "}
{llmConfig.IMAGE_PROVIDER &&
IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]
IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]
? IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER].label
: "_____"}{" "}
for images
@ -1101,24 +1099,23 @@ export default function Home() {
(llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||
(llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL)
}
className={`mt-8 w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${
isLoading ||
className={`mt-8 w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${isLoading ||
(llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||
(llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL)
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}
>
{isLoading ? (
<div className="flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
{(llmConfig.LLM === "ollama" && downloadingModel.downloaded) ||
0 > 0
0 > 0
? `Downloading Model (${(
((downloadingModel.downloaded || 0) /
(downloadingModel.size || 1)) *
100
).toFixed(0)}%)`
((downloadingModel.downloaded || 0) /
(downloadingModel.size || 1)) *
100
).toFixed(0)}%)`
: "Saving Configuration..."}
</div>
) : (llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) ||