diff --git a/.env.example b/.env.example index ff37fbbad..faf548e0c 100644 --- a/.env.example +++ b/.env.example @@ -18,7 +18,7 @@ API_PORT=8000 # Supported languages: en_US, en_GB, zh-Hans, zh-Hant # You can interact with the agent in your preferred language. # This variable is mainly intended for frontend use; setting LANG is not required. -LANG=en-US +LANG=en_US TIMEZONE=America/New_York PYTHONIOENCODING=utf-8 diff --git a/frontend/biome.json b/frontend/biome.json index af0c2fafd..477e5fa44 100644 --- a/frontend/biome.json +++ b/frontend/biome.json @@ -58,12 +58,8 @@ "fix": "safe", "level": "error", "options": { - "attributes": [ - "className" - ], - "functions": [ - "cn" - ] + "attributes": ["className"], + "functions": ["cn"] } } }, @@ -84,4 +80,4 @@ "enabled": true, "useIgnoreFile": true } -} \ No newline at end of file +} diff --git a/frontend/src/root.tsx b/frontend/src/root.tsx index d97966193..46e301c09 100644 --- a/frontend/src/root.tsx +++ b/frontend/src/root.tsx @@ -1,5 +1,7 @@ +import { useEffect, useRef } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; +import { toast } from "sonner"; import AppSidebar from "@/components/valuecell/app-sidebar"; import { Toaster } from "./components/ui/sonner"; @@ -42,6 +44,69 @@ const queryClient = new QueryClient({ }); export default function Root() { + const bannerShownRef = useRef(false); + + useEffect(() => { + const checkApiConfig = async () => { + if (bannerShownRef.current) { + return; + } + + try { + const baseUrl = + import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api/v1"; + + const response = await fetch(`${baseUrl}/system/health`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + bannerShownRef.current = true; + toast.error( + "⚠️ No LLM APIs configured. Please set up API keys in your .env file", + { + duration: Infinity, + description: + "Add API keys for OpenRouter, SiliconFlow, Google, or OpenAI", + } + ); + return; + } + + const data = await response.json(); + + if (data?.data?.api_configured === false) { + bannerShownRef.current = true; + toast.error( + "⚠️ No LLM APIs configured. Please set up API keys in your .env file", + { + duration: Infinity, + description: + "Add API keys for OpenRouter, SiliconFlow, Google, or OpenAI", + } + ); + } else if (data?.data?.api_configured === true) { + bannerShownRef.current = true; + } + } catch (_error) { + bannerShownRef.current = true; + toast.error( + "⚠️ No LLM APIs configured. Please set up API keys in your .env file", + { + duration: Infinity, + description: + "Add API keys for OpenRouter, SiliconFlow, Google, or OpenAI", + } + ); + } + }; + + checkApiConfig(); + }, []); + return ( diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index bd174d41c..452f47459 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -7,7 +7,10 @@ import tsconfigPaths from "vite-tsconfig-paths"; const host = process.env.TAURI_DEV_HOST; // https://vite.dev/config/ + + export default defineConfig(async () => ({ + plugins: [ tailwindcss(), reactRouter(), diff --git a/python/valuecell/server/api/routers/system.py b/python/valuecell/server/api/routers/system.py index 820a34330..9258ad756 100644 --- a/python/valuecell/server/api/routers/system.py +++ b/python/valuecell/server/api/routers/system.py @@ -4,6 +4,8 @@ from fastapi import APIRouter +from valuecell.config.manager import ConfigManager + from ...config.settings import get_settings from ..schemas import AppInfoData, HealthCheckData, SuccessResponse @@ -38,8 +40,15 @@ async def get_app_info(): ) async def health_check(): """Service health status check.""" + config_manager = ConfigManager() + enabled_providers = config_manager.get_enabled_providers() + health_data = HealthCheckData( - status="healthy", version=settings.APP_VERSION, timestamp=datetime.now() + status="healthy", + version=settings.APP_VERSION, + timestamp=datetime.now(), + api_configured=len(enabled_providers) > 0, + available_providers=enabled_providers, ) return SuccessResponse.create( data=health_data, msg="Service is running normally" diff --git a/python/valuecell/server/api/schemas/base.py b/python/valuecell/server/api/schemas/base.py index 6449a5845..b5d4e9c70 100644 --- a/python/valuecell/server/api/schemas/base.py +++ b/python/valuecell/server/api/schemas/base.py @@ -2,7 +2,7 @@ from datetime import datetime from enum import IntEnum -from typing import Generic, Optional, TypeVar +from typing import Generic, List, Optional, TypeVar from pydantic import BaseModel, Field @@ -73,3 +73,7 @@ class HealthCheckData(BaseModel): status: str = Field(..., description="Service status") version: str = Field(..., description="Service version") timestamp: Optional[datetime] = Field(None, description="Check timestamp") + api_configured: bool = Field(..., description="Whether LLM APIs are configured") + available_providers: List[str] = Field( + default_factory=list, description="List of available LLM providers" + )