Skip to content

Commit f268b23

Browse files
authored
feat: Add language configuration and API validation (#186)
I've added a language configuration file `lang.config.json` to define selectable languages for content generation. The API endpoints for chat and wiki caching have been updated to validate the selected language against this configuration. Requests with unsupported languages will now be rejected with an appropriate error message. Key changes: - Created `api/config/lang.json` with an initial set of supported languages. - Modified `api/config.py` to load this configuration. - Implemented language validation in: - `api/simple_chat.py` (chat_completions_stream) - `api/websocket_wiki.py` (handle_websocket_chat) - `api/api.py` (get_cached_wiki, store_wiki_cache, delete_wiki_cache)
1 parent e5f5ae0 commit f268b23

File tree

9 files changed

+169
-72
lines changed

9 files changed

+169
-72
lines changed

api/api.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class AuthorizationConfig(BaseModel):
128128

129129
from api.config import configs, WIKI_AUTH_MODE, WIKI_AUTH_CODE
130130

131+
@app.get("/lang/config")
132+
async def get_lang_config():
133+
return configs["lang_config"]
134+
131135
@app.get("/auth/status")
132136
async def get_auth_status():
133137
"""
@@ -444,6 +448,11 @@ async def get_cached_wiki(
444448
"""
445449
Retrieves cached wiki data (structure and generated pages) for a repository.
446450
"""
451+
# Language validation
452+
supported_langs = configs["lang_config"]["supported_languages"]
453+
if not supported_langs.__contains__(language):
454+
language = configs["lang_config"]["default"]
455+
447456
logger.info(f"Attempting to retrieve wiki cache for {owner}/{repo} ({repo_type}), lang: {language}")
448457
cached_data = await read_wiki_cache(owner, repo, repo_type, language)
449458
if cached_data:
@@ -459,6 +468,12 @@ async def store_wiki_cache(request_data: WikiCacheRequest):
459468
"""
460469
Stores generated wiki data (structure and pages) to the server-side cache.
461470
"""
471+
# Language validation
472+
supported_langs = configs["lang_config"]["supported_languages"]
473+
474+
if not supported_langs.__contains__(request_data.language):
475+
request_data.language = configs["lang_config"]["default"]
476+
462477
logger.info(f"Attempting to save wiki cache for {request_data.owner}/{request_data.repo} ({request_data.repo_type}), lang: {request_data.language}")
463478
success = await save_wiki_cache(request_data)
464479
if success:
@@ -472,11 +487,16 @@ async def delete_wiki_cache(
472487
repo: str = Query(..., description="Repository name"),
473488
repo_type: str = Query(..., description="Repository type (e.g., github, gitlab)"),
474489
language: str = Query(..., description="Language of the wiki content"),
475-
authorization_code: str = Query(..., description="Authorization code")
490+
authorization_code: Optional[str] = Query(None, description="Authorization code")
476491
):
477492
"""
478493
Deletes a specific wiki cache from the file system.
479494
"""
495+
# Language validation
496+
supported_langs = configs["lang_config"]["supported_languages"]
497+
if not supported_langs.__contains__(language):
498+
raise HTTPException(status_code=400, detail="Language is not supported")
499+
480500
if WIKI_AUTH_MODE:
481501
logger.info("check the authorization code")
482502
if WIKI_AUTH_CODE != authorization_code:

api/config.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,40 @@ def is_ollama_embedder():
179179
def load_repo_config():
180180
return load_json_config("repo.json")
181181

182+
# Load language configuration
183+
def load_lang_config():
184+
default_config = {
185+
"supported_languages": {
186+
"en": "English",
187+
"ja": "Japanese (日本語)",
188+
"zh": "Mandarin Chinese (中文)",
189+
"es": "Spanish (Español)",
190+
"kr": "Korean (한국어)",
191+
"vi": "Vietnamese (Tiếng Việt)"
192+
},
193+
"default": "en"
194+
}
195+
try:
196+
# If environment variable is set, use the directory specified by it
197+
if CONFIG_DIR:
198+
config_path = Path(CONFIG_DIR) / "lang.json"
199+
else:
200+
# Otherwise use default directory
201+
config_path = Path(__file__).parent / "config" / "lang.json"
202+
203+
logger.info(f"Loading language configuration from {config_path}")
204+
205+
if not config_path.exists():
206+
logger.warning(f"Language configuration file {config_path} does not exist")
207+
return default_config
208+
return load_json_config(config_path)
209+
except json.JSONDecodeError as e:
210+
logger.error(f"Error decoding JSON from language configuration file {config_path}: {str(e)}")
211+
return default_config
212+
except Exception as e:
213+
logger.error(f"Error loading language configuration file {config_path}: {str(e)}")
214+
return default_config
215+
182216
# Default excluded directories and files
183217
DEFAULT_EXCLUDED_DIRS: List[str] = [
184218
# Virtual environments and package managers
@@ -227,6 +261,7 @@ def load_repo_config():
227261
generator_config = load_generator_config()
228262
embedder_config = load_embedder_config()
229263
repo_config = load_repo_config()
264+
lang_config = load_lang_config()
230265

231266
# Update configuration
232267
if generator_config:
@@ -245,6 +280,11 @@ def load_repo_config():
245280
if key in repo_config:
246281
configs[key] = repo_config[key]
247282

283+
# Update language configuration
284+
if lang_config:
285+
configs["lang_config"] = lang_config
286+
287+
248288
def get_model_config(provider="google", model=None):
249289
"""
250290
Get configuration for the specified provider and model

api/config/lang.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"supported_languages": {
3+
"en": "English",
4+
"ja": "Japanese (日本語)",
5+
"zh": "Mandarin Chinese (中文)",
6+
"es": "Spanish (Español)",
7+
"kr": "Korean (한국어)",
8+
"vi": "Vietnamese (Tiếng Việt)"
9+
},
10+
"default": "en"
11+
}

api/simple_chat.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from fastapi.responses import StreamingResponse
1212
from pydantic import BaseModel, Field
1313

14-
from api.config import get_model_config
14+
from api.config import get_model_config, configs
1515
from api.data_pipeline import count_tokens, get_file_content
1616
from api.openai_client import OpenAIClient
1717
from api.openrouter_client import OpenRouterClient
@@ -245,15 +245,9 @@ async def chat_completions_stream(request: ChatCompletionRequest):
245245
repo_type = request.type
246246

247247
# Get language information
248-
language_code = request.language or "en"
249-
language_name = {
250-
"en": "English",
251-
"ja": "Japanese (日本語)",
252-
"zh": "Mandarin Chinese (中文)",
253-
"es": "Spanish (Español)",
254-
"kr": "Korean (한국어)",
255-
"vi": "Vietnamese (Tiếng Việt)"
256-
}.get(language_code, "English")
248+
language_code = request.language or configs["lang_config"]["default"]
249+
supported_langs = configs["lang_config"]["supported_languages"]
250+
language_name = supported_langs.get(language_code, "English")
257251

258252
# Create system prompt
259253
if is_deep_research:

api/websocket_wiki.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastapi import WebSocket, WebSocketDisconnect, HTTPException
1010
from pydantic import BaseModel, Field
1111

12-
from api.config import get_model_config
12+
from api.config import get_model_config, configs
1313
from api.data_pipeline import count_tokens, get_file_content
1414
from api.openai_client import OpenAIClient
1515
from api.openrouter_client import OpenRouterClient
@@ -245,15 +245,9 @@ async def handle_websocket_chat(websocket: WebSocket):
245245
repo_type = request.type
246246

247247
# Get language information
248-
language_code = request.language or "en"
249-
language_name = {
250-
"en": "English",
251-
"ja": "Japanese (日本語)",
252-
"zh": "Mandarin Chinese (中文)",
253-
"es": "Spanish (Español)",
254-
"kr": "Korean (한국어)",
255-
"vi": "Vietnamese (Tiếng Việt)"
256-
}.get(language_code, "English")
248+
language_code = request.language or configs["lang_config"]["default"]
249+
supported_langs = configs["lang_config"]["supported_languages"]
250+
language_name = supported_langs.get(language_code, "English")
257251

258252
# Create system prompt
259253
if is_deep_research:

next.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ const nextConfig: NextConfig = {
5959
source: '/api/auth/validate',
6060
destination: `${TARGET_SERVER_BASE_URL}/auth/validate`,
6161
},
62+
{
63+
source: '/api/lang/config',
64+
destination: `${TARGET_SERVER_BASE_URL}/lang/config`,
65+
},
6266
];
6367
},
6468
};

src/app/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const DEMO_SEQUENCE_CHART = `sequenceDiagram
4444

4545
export default function Home() {
4646
const router = useRouter();
47-
const { language, setLanguage, messages } = useLanguage();
47+
const { language, setLanguage, messages, supportedLanguages } = useLanguage();
4848
const { projects, isLoading: projectsLoading } = useProcessedProjects();
4949

5050
// Create a simple translation function
@@ -365,6 +365,7 @@ export default function Home() {
365365
repositoryInput={repositoryInput}
366366
selectedLanguage={selectedLanguage}
367367
setSelectedLanguage={setSelectedLanguage}
368+
supportedLanguages={supportedLanguages}
368369
isComprehensiveView={isComprehensiveView}
369370
setIsComprehensiveView={setIsComprehensiveView}
370371
provider={provider}

src/components/ConfigurationModal.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface ConfigurationModalProps {
1515
// Language selection
1616
selectedLanguage: string;
1717
setSelectedLanguage: (value: string) => void;
18+
supportedLanguages: Record<string, string>;
1819

1920
// Wiki type options
2021
isComprehensiveView: boolean;
@@ -65,6 +66,7 @@ export default function ConfigurationModal({
6566
repositoryInput,
6667
selectedLanguage,
6768
setSelectedLanguage,
69+
supportedLanguages,
6870
isComprehensiveView,
6971
setIsComprehensiveView,
7072
provider,
@@ -144,12 +146,9 @@ export default function ConfigurationModal({
144146
onChange={(e) => setSelectedLanguage(e.target.value)}
145147
className="input-japanese block w-full px-3 py-2 text-sm rounded-md bg-transparent text-[var(--foreground)] focus:outline-none focus:border-[var(--accent-primary)]"
146148
>
147-
<option value="en">English</option>
148-
<option value="ja">Japanese (日本語)</option>
149-
<option value="zh">Mandarin (中文)</option>
150-
<option value="es">Spanish (Español)</option>
151-
<option value="kr">Korean (한국어)</option>
152-
<option value="vi">Vietnamese (Tiếng Việt)</option>
149+
{
150+
Object.entries(supportedLanguages).map(([key, value])=> <option key={key} value={key}>{value}</option>)
151+
}
153152
</select>
154153
</div>
155154

0 commit comments

Comments
 (0)