Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 3 additions & 7 deletions frontend/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,8 @@
"fix": "safe",
"level": "error",
"options": {
"attributes": [
"className"
],
"functions": [
"cn"
]
"attributes": ["className"],
"functions": ["cn"]
}
}
},
Expand All @@ -84,4 +80,4 @@
"enabled": true,
"useIgnoreFile": true
}
}
}
65 changes: 65 additions & 0 deletions frontend/src/root.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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 (
<QueryClientProvider client={queryClient}>
<SidebarProvider>
Expand Down
3 changes: 3 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
11 changes: 10 additions & 1 deletion python/valuecell/server/api/routers/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⁠healthz is a high-frequency endpoint. We shouldn't include such a heavy query here.


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"
Expand Down
6 changes: 5 additions & 1 deletion python/valuecell/server/api/schemas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
)