Merge pull request #56 from presenton/feat/ollama_api
fix(fastapi, nextjs): fixes errors while using Custom Ollama URL
This commit is contained in:
commit
39d5a130ee
14 changed files with 223 additions and 106 deletions
|
|
@ -1,5 +1,25 @@
|
|||
services:
|
||||
production:
|
||||
# image: ghcr.io/presenton/presenton:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
# You can replace 5000 with any other port number of your choice to run Presenton on a different port number.
|
||||
- "5000:80"
|
||||
volumes:
|
||||
- ./user_data:/app/user_data
|
||||
environment:
|
||||
- CAN_CHANGE_KEYS=${CAN_CHANGE_KEYS}
|
||||
- LLM=${LLM}
|
||||
- LLM_PROVIDER_URL=${LLM_PROVIDER_URL}
|
||||
- LLM_API_KEY=${LLM_API_KEY}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL}
|
||||
- PEXELS_API_KEY=${PEXELS_API_KEY}
|
||||
|
||||
production-gpu:
|
||||
# image: ghcr.io/presenton/presenton:latest
|
||||
build:
|
||||
context: .
|
||||
|
|
@ -19,12 +39,35 @@ services:
|
|||
environment:
|
||||
- CAN_CHANGE_KEYS=${CAN_CHANGE_KEYS}
|
||||
- LLM=${LLM}
|
||||
- LLM_PROVIDER_URL=${LLM_PROVIDER_URL}
|
||||
- LLM_API_KEY=${LLM_API_KEY}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL}
|
||||
- PEXELS_API_KEY=${PEXELS_API_KEY}
|
||||
|
||||
development:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
ports:
|
||||
- "5000:80"
|
||||
- "3000:3000"
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- .:/app
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- CAN_CHANGE_KEYS=${CAN_CHANGE_KEYS}
|
||||
- LLM=${LLM}
|
||||
- LLM_PROVIDER_URL=${LLM_PROVIDER_URL}
|
||||
- LLM_API_KEY=${LLM_API_KEY}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL}
|
||||
- PEXELS_API_KEY=${PEXELS_API_KEY}
|
||||
|
||||
development-gpu:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
|
|
@ -45,6 +88,8 @@ services:
|
|||
- NODE_ENV=development
|
||||
- CAN_CHANGE_KEYS=${CAN_CHANGE_KEYS}
|
||||
- LLM=${LLM}
|
||||
- LLM_PROVIDER_URL=${LLM_PROVIDER_URL}
|
||||
- LLM_API_KEY=${LLM_API_KEY}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class PresentationEditHandler:
|
|||
icons=None,
|
||||
presentation=slide_to_edit.presentation,
|
||||
properties=slide_to_edit.properties,
|
||||
content=edited_content,
|
||||
content=edited_content.to_content(),
|
||||
)
|
||||
|
||||
new_slide_images_count = new_slide_model.images_count
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import os
|
||||
import aiohttp
|
||||
from fastapi import HTTPException
|
||||
from api.models import LogMetadata
|
||||
from api.routers.presentation.models import OllamaModelStatusResponse
|
||||
from api.services.logging import LoggingService
|
||||
from api.utils.model_utils import get_llm_api_key_or, get_llm_provider_url_or
|
||||
from api.utils.model_utils import (
|
||||
get_llm_provider_url_or,
|
||||
get_ollama_request_headers,
|
||||
)
|
||||
|
||||
|
||||
class ListPulledOllamaModelsHandler:
|
||||
|
|
@ -13,13 +16,23 @@ class ListPulledOllamaModelsHandler:
|
|||
logging_service.message("Listing Ollama models"),
|
||||
extra=log_metadata.model_dump(),
|
||||
)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"{get_llm_provider_url_or()}/api/tags",
|
||||
headers={"Authorization": f"Bearer {get_llm_api_key_or()}"},
|
||||
headers=get_ollama_request_headers(),
|
||||
) as response:
|
||||
response_data = await response.json()
|
||||
if response.status == 200:
|
||||
response_data = await response.json()
|
||||
elif response.status == 403:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Forbidden: Please check your Ollama Configuration",
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=response.status,
|
||||
detail=f"Failed to list Ollama models: {response.status}",
|
||||
)
|
||||
|
||||
logging_service.logger.info(
|
||||
logging_service.message(response_data),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import traceback
|
||||
import aiohttp
|
||||
from fastapi import BackgroundTasks, HTTPException
|
||||
from api.models import LogMetadata
|
||||
|
|
@ -8,7 +9,10 @@ from api.routers.presentation.handlers.list_supported_ollama_models import (
|
|||
from api.routers.presentation.models import OllamaModelStatusResponse
|
||||
from api.services.instances import REDIS_SERVICE
|
||||
from api.services.logging import LoggingService
|
||||
from api.utils.model_utils import get_llm_api_key_or, get_llm_provider_url_or
|
||||
from api.utils.model_utils import (
|
||||
get_llm_provider_url_or,
|
||||
get_ollama_request_headers,
|
||||
)
|
||||
|
||||
|
||||
class PullOllamaModelHandler:
|
||||
|
|
@ -38,7 +42,7 @@ class PullOllamaModelHandler:
|
|||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"{get_llm_provider_url_or()}/api/tags",
|
||||
headers={"Authorization": f"Bearer {get_llm_api_key_or()}"},
|
||||
headers=get_ollama_request_headers(),
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
pulled_models = await response.json()
|
||||
|
|
@ -57,17 +61,49 @@ class PullOllamaModelHandler:
|
|||
downloaded=filtered_models[0]["size"],
|
||||
done=True,
|
||||
)
|
||||
elif response.status == 403:
|
||||
print(response)
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Forbidden: Please check your Ollama Configuration",
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=response.status,
|
||||
detail=f"Failed to list Ollama models: {response.status}",
|
||||
)
|
||||
except HTTPException as e:
|
||||
logging_service.logger.warning(
|
||||
logging_service.message(e.detail),
|
||||
extra=log_metadata.model_dump(),
|
||||
)
|
||||
raise e
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
logging_service.logger.warning(
|
||||
f"Failed to check pulled models: {e}",
|
||||
extra=log_metadata.model_dump(),
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to check pulled models: {e}",
|
||||
)
|
||||
|
||||
saved_model_status = REDIS_SERVICE.get(f"ollama_models/{self.name}")
|
||||
|
||||
# If the model is being pulled, return the model
|
||||
if saved_model_status:
|
||||
return json.loads(saved_model_status)
|
||||
saved_model_status_json = json.loads(saved_model_status)
|
||||
# If the model is being pulled, return the model
|
||||
# ? If the model status is pulled in redis but was not found while listing pulled models,
|
||||
# ? it means the model was deleted and we need to pull it again
|
||||
if (
|
||||
saved_model_status_json["status"] == "error"
|
||||
or saved_model_status_json["status"] == "pulled"
|
||||
):
|
||||
REDIS_SERVICE.delete(f"ollama_models/{self.name}")
|
||||
else:
|
||||
return saved_model_status_json
|
||||
|
||||
# If the model is not being pulled, pull the model
|
||||
background_tasks.add_task(self.pull_model_in_background)
|
||||
|
|
@ -93,8 +129,8 @@ class PullOllamaModelHandler:
|
|||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{get_llm_provider_url_or()}/api/pull",
|
||||
headers=get_ollama_request_headers(),
|
||||
json={"model": self.name},
|
||||
headers={"Authorization": f"Bearer {get_llm_api_key_or()}"},
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
raise HTTPException(
|
||||
|
|
@ -136,7 +172,10 @@ class PullOllamaModelHandler:
|
|||
f"ollama_models/{self.name}",
|
||||
json.dumps(saved_model_status.model_dump(mode="json")),
|
||||
)
|
||||
raise e
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to pull model: {e}",
|
||||
)
|
||||
|
||||
saved_model_status.done = True
|
||||
saved_model_status.status = "pulled"
|
||||
|
|
|
|||
|
|
@ -10,11 +10,18 @@ def is_ollama_selected() -> bool:
|
|||
|
||||
|
||||
def get_llm_provider_url_or():
|
||||
return os.getenv("LLM_PROVIDER_URL") or "http://localhost:11434"
|
||||
llm_provider_url = os.getenv("LLM_PROVIDER_URL") or "http://localhost:11434"
|
||||
if llm_provider_url.endswith("/"):
|
||||
return llm_provider_url[:-1]
|
||||
return llm_provider_url
|
||||
|
||||
|
||||
def get_llm_api_key_or():
|
||||
return os.getenv("LLM_API_KEY") or "ollama"
|
||||
def get_ollama_request_headers():
|
||||
if os.getenv("LLM_API_KEY"):
|
||||
return {
|
||||
"Authorization": f"Bearer {os.getenv('LLM_API_KEY')}",
|
||||
}
|
||||
return {}
|
||||
|
||||
|
||||
def get_selected_llm_provider() -> SelectedLLMProvider:
|
||||
|
|
@ -41,7 +48,7 @@ def get_llm_api_key():
|
|||
elif selected_llm == SelectedLLMProvider.GOOGLE:
|
||||
return os.getenv("GOOGLE_API_KEY")
|
||||
elif selected_llm == SelectedLLMProvider.OLLAMA:
|
||||
return get_llm_api_key_or()
|
||||
return os.getenv("LLM_API_KEY") or "ollama"
|
||||
else:
|
||||
raise ValueError(f"Invalid LLM API key")
|
||||
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ def get_user_config():
|
|||
|
||||
return UserConfig(
|
||||
LLM=existing_config.LLM or os.getenv("LLM"),
|
||||
LLM_PROVIDER_URL=existing_config.LLM_PROVIDER_URL
|
||||
or os.getenv("LLM_PROVIDER_URL"),
|
||||
LLM_API_KEY=existing_config.LLM_API_KEY or os.getenv("LLM_API_KEY"),
|
||||
OPENAI_API_KEY=existing_config.OPENAI_API_KEY or os.getenv("OPENAI_API_KEY"),
|
||||
GOOGLE_API_KEY=existing_config.GOOGLE_API_KEY or os.getenv("GOOGLE_API_KEY"),
|
||||
MODEL=existing_config.MODEL or os.getenv("MODEL"),
|
||||
PEXELS_API_KEY=existing_config.PEXELS_API_KEY or os.getenv("PEXELS_API_KEY"),
|
||||
LLM_PROVIDER_URL=existing_config.LLM_PROVIDER_URL
|
||||
or os.getenv("LLM_PROVIDER_URL"),
|
||||
LLM_API_KEY=existing_config.LLM_API_KEY or os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -56,6 +56,10 @@ def update_env_with_user_config():
|
|||
user_config = get_user_config()
|
||||
if user_config.LLM:
|
||||
os.environ["LLM"] = user_config.LLM
|
||||
if user_config.LLM_PROVIDER_URL:
|
||||
os.environ["LLM_PROVIDER_URL"] = user_config.LLM_PROVIDER_URL
|
||||
if user_config.LLM_API_KEY:
|
||||
os.environ["LLM_API_KEY"] = user_config.LLM_API_KEY
|
||||
if user_config.OPENAI_API_KEY:
|
||||
os.environ["OPENAI_API_KEY"] = user_config.OPENAI_API_KEY
|
||||
if user_config.GOOGLE_API_KEY:
|
||||
|
|
@ -64,10 +68,6 @@ def update_env_with_user_config():
|
|||
os.environ["MODEL"] = user_config.MODEL
|
||||
if user_config.PEXELS_API_KEY:
|
||||
os.environ["PEXELS_API_KEY"] = user_config.PEXELS_API_KEY
|
||||
if user_config.LLM_PROVIDER_URL:
|
||||
os.environ["LLM_PROVIDER_URL"] = user_config.LLM_PROVIDER_URL
|
||||
if user_config.LLM_API_KEY:
|
||||
os.environ["LLM_API_KEY"] = user_config.LLM_API_KEY
|
||||
|
||||
|
||||
def get_resource(relative_path):
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import os
|
|||
import uvicorn
|
||||
import argparse
|
||||
|
||||
from api.main import app
|
||||
|
||||
|
||||
# ? Helps pyinstaller for dependencies resolution
|
||||
app
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.makedirs("debug", exist_ok=True)
|
||||
|
|
|
|||
|
|
@ -34,12 +34,13 @@ export async function POST(request: Request) {
|
|||
}
|
||||
const mergedConfig: LLMConfig = {
|
||||
LLM: userConfig.LLM || existingConfig.LLM,
|
||||
LLM_PROVIDER_URL: userConfig.LLM_PROVIDER_URL || existingConfig.LLM_PROVIDER_URL,
|
||||
LLM_API_KEY: userConfig.LLM_API_KEY,
|
||||
OPENAI_API_KEY: userConfig.OPENAI_API_KEY || existingConfig.OPENAI_API_KEY,
|
||||
GOOGLE_API_KEY: userConfig.GOOGLE_API_KEY || existingConfig.GOOGLE_API_KEY,
|
||||
MODEL: userConfig.MODEL || existingConfig.MODEL,
|
||||
LLM_PROVIDER_URL: userConfig.LLM_PROVIDER_URL || existingConfig.LLM_PROVIDER_URL,
|
||||
LLM_API_KEY: userConfig.LLM_API_KEY || existingConfig.LLM_API_KEY,
|
||||
PEXELS_API_KEY: userConfig.PEXELS_API_KEY || existingConfig.PEXELS_API_KEY,
|
||||
USE_CUSTOM_URL: userConfig.USE_CUSTOM_URL === undefined ? existingConfig.USE_CUSTOM_URL : userConfig.USE_CUSTOM_URL,
|
||||
}
|
||||
fs.writeFileSync(userConfigPath, JSON.stringify(mergedConfig))
|
||||
return NextResponse.json(mergedConfig)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const SettingsPage = () => {
|
|||
});
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [openModelSelect, setOpenModelSelect] = useState(false);
|
||||
const [useCustomOllamaUrl, setUseCustomOllamaUrl] = useState<boolean>(false);
|
||||
const [useCustomOllamaUrl, setUseCustomOllamaUrl] = useState<boolean>(userConfigState.llm_config.USE_CUSTOM_URL || false);
|
||||
|
||||
const api_key_changed = (apiKey: string, field?: string) => {
|
||||
if (llmConfig.LLM === 'openai') {
|
||||
|
|
@ -91,27 +91,12 @@ const SettingsPage = () => {
|
|||
}
|
||||
|
||||
const handleSaveConfig = async () => {
|
||||
if (llmConfig.LLM === 'ollama') {
|
||||
try {
|
||||
try {
|
||||
await handleSaveLLMConfig(llmConfig);
|
||||
if (llmConfig.LLM === 'ollama') {
|
||||
setIsLoading(true);
|
||||
await pullOllamaModels();
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Model downloaded successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error pulling model:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to download model. Please try again.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await handleSaveLLMConfig(llmConfig, useCustomOllamaUrl);
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Configuration saved successfully',
|
||||
|
|
@ -122,7 +107,7 @@ const SettingsPage = () => {
|
|||
console.error('Error:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to save configuration',
|
||||
description: error instanceof Error ? error.message : 'Failed to save configuration',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsLoading(false);
|
||||
|
|
@ -136,34 +121,48 @@ const SettingsPage = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const resetDownloadingModel = () => {
|
||||
setDownloadingModel({
|
||||
name: '',
|
||||
size: null,
|
||||
downloaded: null,
|
||||
status: '',
|
||||
done: false,
|
||||
});
|
||||
}
|
||||
|
||||
const pullOllamaModels = async (): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/ppt/ollama/pull-model?name=${llmConfig.MODEL}`);
|
||||
if (response.status === 200) {
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.done) {
|
||||
if (data.done && data.status !== 'error') {
|
||||
clearInterval(interval);
|
||||
setDownloadingModel(data);
|
||||
resolve();
|
||||
} else if (data.status === 'error') {
|
||||
clearInterval(interval);
|
||||
resetDownloadingModel();
|
||||
reject(new Error('Error occurred while pulling model'));
|
||||
} else {
|
||||
setDownloadingModel(data);
|
||||
}
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
reject(new Error('Model pulling failed'));
|
||||
resetDownloadingModel();
|
||||
if (response.status === 403) {
|
||||
reject(new Error('Request to Ollama Not Authorized'));
|
||||
}
|
||||
reject(new Error('Error occurred while pulling model'));
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
console.log('Error fetching ollama models:', error);
|
||||
clearInterval(interval);
|
||||
resetDownloadingModel();
|
||||
reject(error);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +176,14 @@ const SettingsPage = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const setOllamaConfig = () => {
|
||||
if (!useCustomOllamaUrl) {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: 'http://localhost:11434', LLM_API_KEY: undefined, USE_CUSTOM_URL: false });
|
||||
} else {
|
||||
setLlmConfig({ ...llmConfig, USE_CUSTOM_URL: true });
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (!canChangeKeys) {
|
||||
|
|
@ -188,11 +195,7 @@ const SettingsPage = () => {
|
|||
}, [userConfigState.llm_config.LLM]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!useCustomOllamaUrl) {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: undefined, LLM_API_KEY: undefined });
|
||||
} else {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: 'http://localhost:11434', LLM_API_KEY: '' });
|
||||
}
|
||||
setOllamaConfig();
|
||||
}, [useCustomOllamaUrl]);
|
||||
|
||||
if (!canChangeKeys) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export function StoreInitializer({ children }: { children: React.ReactNode }) {
|
|||
llmConfig.LLM = 'openai';
|
||||
}
|
||||
dispatch(setLLMConfig(llmConfig));
|
||||
const isValid = hasValidLLMConfig(llmConfig, false);
|
||||
const isValid = hasValidLLMConfig(llmConfig);
|
||||
if (isValid) {
|
||||
// Check if the selected Ollama model is pulled
|
||||
if (llmConfig.LLM === 'ollama') {
|
||||
|
|
@ -75,10 +75,15 @@ export function StoreInitializer({ children }: { children: React.ReactNode }) {
|
|||
}
|
||||
|
||||
const checkIfSelectedOllamaModelIsPulled = async (ollamaModel: string) => {
|
||||
const response = await fetch('/api/v1/ppt/ollama/list-pulled-models');
|
||||
const data = await response.json();
|
||||
const pulledModels = data.map((model: any) => model.name);
|
||||
return pulledModels.includes(ollamaModel);
|
||||
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);
|
||||
return pulledModels.includes(ollamaModel);
|
||||
} catch (error) {
|
||||
console.error('Error checking if selected Ollama model is pulled:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ export default function Home() {
|
|||
});
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [openModelSelect, setOpenModelSelect] = useState(false);
|
||||
const [useCustomOllamaUrl, setUseCustomOllamaUrl] = useState<boolean>(false);
|
||||
const [useCustomOllamaUrl, setUseCustomOllamaUrl] = useState<boolean>(llmConfig.USE_CUSTOM_URL || false);
|
||||
|
||||
const canChangeKeys = config.can_change_keys;
|
||||
|
||||
|
|
@ -180,27 +180,12 @@ export default function Home() {
|
|||
}
|
||||
|
||||
const handleSaveConfig = async () => {
|
||||
if (llmConfig.LLM === 'ollama') {
|
||||
try {
|
||||
try {
|
||||
await handleSaveLLMConfig(llmConfig);
|
||||
if (llmConfig.LLM === 'ollama') {
|
||||
setIsLoading(true);
|
||||
await pullOllamaModels();
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Model downloaded successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error pulling model:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to download model. Please try again.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await handleSaveLLMConfig(llmConfig, useCustomOllamaUrl);
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Configuration saved successfully',
|
||||
|
|
@ -211,7 +196,7 @@ export default function Home() {
|
|||
console.error('Error:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to save configuration',
|
||||
description: error instanceof Error ? error.message : 'Failed to save configuration',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsLoading(false);
|
||||
|
|
@ -225,6 +210,16 @@ export default function Home() {
|
|||
}
|
||||
}
|
||||
|
||||
const resetDownloadingModel = () => {
|
||||
setDownloadingModel({
|
||||
name: '',
|
||||
size: null,
|
||||
downloaded: null,
|
||||
status: '',
|
||||
done: false,
|
||||
});
|
||||
}
|
||||
|
||||
const pullOllamaModels = async (): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const interval = setInterval(async () => {
|
||||
|
|
@ -232,21 +227,28 @@ export default function Home() {
|
|||
const response = await fetch(`/api/v1/ppt/ollama/pull-model?name=${llmConfig.MODEL}`);
|
||||
if (response.status === 200) {
|
||||
const data = await response.json();
|
||||
|
||||
if (data.done) {
|
||||
if (data.done && data.status !== 'error') {
|
||||
clearInterval(interval);
|
||||
setDownloadingModel(data);
|
||||
resolve();
|
||||
} else if (data.status === 'error') {
|
||||
clearInterval(interval);
|
||||
resetDownloadingModel();
|
||||
reject(new Error('Error occurred while pulling model'));
|
||||
} else {
|
||||
setDownloadingModel(data);
|
||||
}
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
reject(new Error('Model pulling failed'));
|
||||
resetDownloadingModel();
|
||||
if (response.status === 403) {
|
||||
reject(new Error('Request to Ollama Not Authorized'));
|
||||
}
|
||||
reject(new Error('Error occurred while pulling model'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error fetching ollama models:', error);
|
||||
clearInterval(interval);
|
||||
resetDownloadingModel();
|
||||
reject(error);
|
||||
}
|
||||
}, 1000);
|
||||
|
|
@ -263,6 +265,14 @@ export default function Home() {
|
|||
}
|
||||
}
|
||||
|
||||
const setOllamaConfig = () => {
|
||||
if (!useCustomOllamaUrl) {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: 'http://localhost:11434', LLM_API_KEY: undefined, USE_CUSTOM_URL: false });
|
||||
} else {
|
||||
setLlmConfig({ ...llmConfig, USE_CUSTOM_URL: true });
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!canChangeKeys) {
|
||||
router.push("/upload");
|
||||
|
|
@ -273,11 +283,7 @@ export default function Home() {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!useCustomOllamaUrl) {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: undefined, LLM_API_KEY: undefined });
|
||||
} else {
|
||||
setLlmConfig({ ...llmConfig, LLM_PROVIDER_URL: 'http://localhost:11434', LLM_API_KEY: '' });
|
||||
}
|
||||
setOllamaConfig();
|
||||
}, [useCustomOllamaUrl]);
|
||||
|
||||
|
||||
|
|
|
|||
7
servers/nextjs/types/global.d.ts
vendored
7
servers/nextjs/types/global.d.ts
vendored
|
|
@ -15,10 +15,13 @@ interface TextFrameProps {
|
|||
|
||||
interface LLMConfig {
|
||||
LLM?: string;
|
||||
LLM_PROVIDER_URL?: string;
|
||||
LLM_API_KEY?: string;
|
||||
OPENAI_API_KEY?: string;
|
||||
GOOGLE_API_KEY?: string;
|
||||
PEXELS_API_KEY?: string;
|
||||
LLM_PROVIDER_URL?: string;
|
||||
LLM_API_KEY?: string;
|
||||
MODEL?: string;
|
||||
|
||||
// Only used in UI settings
|
||||
USE_CUSTOM_URL?: boolean;
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { setLLMConfig } from "@/store/slices/userConfig";
|
||||
import { store } from "@/store/store";
|
||||
|
||||
export const handleSaveLLMConfig = async (llmConfig: LLMConfig, useCustomOllamaUrl: boolean) => {
|
||||
if (!hasValidLLMConfig(llmConfig, useCustomOllamaUrl)) {
|
||||
export const handleSaveLLMConfig = async (llmConfig: LLMConfig) => {
|
||||
if (!hasValidLLMConfig(llmConfig)) {
|
||||
throw new Error('API key cannot be empty');
|
||||
}
|
||||
|
||||
|
|
@ -14,21 +14,18 @@ export const handleSaveLLMConfig = async (llmConfig: LLMConfig, useCustomOllamaU
|
|||
store.dispatch(setLLMConfig(llmConfig));
|
||||
}
|
||||
|
||||
export const hasValidLLMConfig = (llmConfig: LLMConfig, useCustomOllamaUrl: boolean) => {
|
||||
export const hasValidLLMConfig = (llmConfig: LLMConfig) => {
|
||||
if (!llmConfig.LLM) return false;
|
||||
const OPENAI_API_KEY = llmConfig.OPENAI_API_KEY;
|
||||
const GOOGLE_API_KEY = llmConfig.GOOGLE_API_KEY;
|
||||
const MODEL = llmConfig.MODEL;
|
||||
const PEXELS_API_KEY = llmConfig.PEXELS_API_KEY;
|
||||
|
||||
const isOllamaBaseConfigValid = PEXELS_API_KEY !== '' && PEXELS_API_KEY !== null && PEXELS_API_KEY !== undefined && MODEL !== '' && MODEL !== null && MODEL !== undefined;
|
||||
const isOllamaConfigValid = PEXELS_API_KEY !== '' && PEXELS_API_KEY !== null && PEXELS_API_KEY !== undefined && MODEL !== '' && MODEL !== null && MODEL !== undefined && llmConfig.LLM_PROVIDER_URL !== '' && llmConfig.LLM_PROVIDER_URL !== null && llmConfig.LLM_PROVIDER_URL !== undefined;
|
||||
|
||||
return llmConfig.LLM === 'openai' ?
|
||||
OPENAI_API_KEY !== '' && OPENAI_API_KEY !== null && OPENAI_API_KEY !== undefined :
|
||||
llmConfig.LLM === 'google' ?
|
||||
GOOGLE_API_KEY !== '' && GOOGLE_API_KEY !== null && GOOGLE_API_KEY !== undefined :
|
||||
llmConfig.LLM === 'ollama' ?
|
||||
useCustomOllamaUrl ?
|
||||
isOllamaBaseConfigValid && llmConfig.LLM_PROVIDER_URL !== '' && llmConfig.LLM_PROVIDER_URL !== null && llmConfig.LLM_PROVIDER_URL !== undefined && llmConfig.LLM_API_KEY !== '' && llmConfig.LLM_API_KEY !== null && llmConfig.LLM_API_KEY !== undefined :
|
||||
isOllamaBaseConfigValid : false;
|
||||
llmConfig.LLM === 'ollama' ? isOllamaConfigValid : false;
|
||||
}
|
||||
5
start.js
5
start.js
|
|
@ -35,10 +35,13 @@ const setupUserConfigFromEnv = () => {
|
|||
|
||||
const userConfig = {
|
||||
LLM: process.env.LLM || existingConfig.LLM,
|
||||
LLM_PROVIDER_URL: process.env.LLM_PROVIDER_URL || existingConfig.LLM_PROVIDER_URL,
|
||||
LLM_API_KEY: process.env.LLM_API_KEY || existingConfig.LLM_API_KEY,
|
||||
OPENAI_API_KEY: process.env.OPENAI_API_KEY || existingConfig.OPENAI_API_KEY,
|
||||
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || existingConfig.GOOGLE_API_KEY,
|
||||
OLLAMA_MODEL: process.env.OLLAMA_MODEL || existingConfig.OLLAMA_MODEL,
|
||||
MODEL: process.env.MODEL || existingConfig.MODEL,
|
||||
PEXELS_API_KEY: process.env.PEXELS_API_KEY || existingConfig.PEXELS_API_KEY,
|
||||
USE_CUSTOM_URL: process.env.USE_CUSTOM_URL || existingConfig.USE_CUSTOM_URL,
|
||||
};
|
||||
|
||||
fs.writeFileSync(userConfigPath, JSON.stringify(userConfig));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue