[AC-AISVC-50] 合入第一个稳定版本 #2
|
|
@ -5,8 +5,10 @@ LLM Provider Factory and Configuration Management.
|
|||
Design pattern: Factory pattern for pluggable LLM providers.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from app.services.llm.base import LLMClient, LLMConfig
|
||||
|
|
@ -14,6 +16,8 @@ from app.services.llm.openai_client import OpenAIClient
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LLM_CONFIG_FILE = Path("config/llm_config.json")
|
||||
|
||||
|
||||
@dataclass
|
||||
class LLMProviderInfo:
|
||||
|
|
@ -257,7 +261,7 @@ class LLMProviderFactory:
|
|||
class LLMConfigManager:
|
||||
"""
|
||||
Manager for LLM configuration.
|
||||
[AC-ASA-16, AC-ASA-17, AC-ASA-18] Configuration management with hot-reload.
|
||||
[AC-ASA-16, AC-ASA-17, AC-ASA-18] Configuration management with hot-reload and persistence.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
|
@ -274,12 +278,41 @@ class LLMConfigManager:
|
|||
"temperature": settings.llm_temperature,
|
||||
}
|
||||
self._client: LLMClient | None = None
|
||||
|
||||
self._load_from_file()
|
||||
|
||||
def _load_from_file(self) -> None:
|
||||
"""Load configuration from file if exists."""
|
||||
try:
|
||||
if LLM_CONFIG_FILE.exists():
|
||||
with open(LLM_CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||||
saved = json.load(f)
|
||||
self._current_provider = saved.get("provider", self._current_provider)
|
||||
saved_config = saved.get("config", {})
|
||||
if saved_config:
|
||||
self._current_config.update(saved_config)
|
||||
logger.info(f"[AC-ASA-16] Loaded LLM config from file: provider={self._current_provider}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[AC-ASA-16] Failed to load LLM config from file: {e}")
|
||||
|
||||
def _save_to_file(self) -> None:
|
||||
"""Save configuration to file."""
|
||||
try:
|
||||
LLM_CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(LLM_CONFIG_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
"provider": self._current_provider,
|
||||
"config": self._current_config,
|
||||
}, f, indent=2, ensure_ascii=False)
|
||||
logger.info(f"[AC-ASA-16] Saved LLM config to file: provider={self._current_provider}")
|
||||
except Exception as e:
|
||||
logger.error(f"[AC-ASA-16] Failed to save LLM config to file: {e}")
|
||||
|
||||
def get_current_config(self) -> dict[str, Any]:
|
||||
"""Get current LLM configuration."""
|
||||
return {
|
||||
"provider": self._current_provider,
|
||||
"config": self._current_config,
|
||||
"config": self._current_config.copy(),
|
||||
}
|
||||
|
||||
async def update_config(
|
||||
|
|
@ -289,7 +322,7 @@ class LLMConfigManager:
|
|||
) -> bool:
|
||||
"""
|
||||
Update LLM configuration.
|
||||
[AC-ASA-16] Hot-reload configuration.
|
||||
[AC-ASA-16] Hot-reload configuration with persistence.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
|
@ -310,6 +343,8 @@ class LLMConfigManager:
|
|||
|
||||
self._current_provider = provider
|
||||
self._current_config = validated_config
|
||||
|
||||
self._save_to_file()
|
||||
|
||||
logger.info(f"[AC-ASA-16] LLM config updated: provider={provider}")
|
||||
return True
|
||||
|
|
@ -365,7 +400,7 @@ class LLMConfigManager:
|
|||
test_provider = provider or self._current_provider
|
||||
test_config = config if config else self._current_config
|
||||
|
||||
logger.info(f"[AC-ASA-17] Test connection: provider={test_provider}, config={test_config}")
|
||||
logger.info(f"[AC-ASA-17] Test connection: provider={test_provider}, model={test_config.get('model')}")
|
||||
|
||||
if test_provider not in LLM_PROVIDERS:
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in New Issue