Commit db05a5fe authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

modify : llm factory file

parent f1a80925
"""
LLM Factory - Centralized LLM creation for OpenAI & Gemini.
Manages initialization and caching of LLM models with automatic provider detection.
LLM Factory - OpenAI LLM creation with caching.
Manages initialization and caching of OpenAI models.
"""
import contextlib
import logging
from langchain_core.language_models import BaseChatModel
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from config import GOOGLE_API_KEY, OPENAI_API_KEY
from config import OPENAI_API_KEY
logger = logging.getLogger(__name__)
class LLMFactory:
"""Singleton factory for managing LLM instances with caching and provider auto-detection."""
"""Singleton factory for managing OpenAI LLM instances with caching."""
COMMON_MODELS: list[str] = [
"gpt-4o-mini",
"gemini-2.0-flash-lite-preview-02-05",
"gpt-4o",
"gpt-5-nano",
"gpt-5-mini",
]
def __init__(self):
......@@ -64,7 +65,7 @@ class LLMFactory:
api_key: str | None = None,
) -> BaseChatModel:
"""
Create and cache a new LLM instance based on model name.
Create and cache a new OpenAI LLM instance.
Args:
model_name: Clean model identifier
......@@ -76,13 +77,10 @@ class LLMFactory:
Configured LLM instance
Raises:
ValueError: If required API keys are missing
ValueError: If API key is missing
"""
try:
if self._is_gemini_model(model_name):
llm = self._create_gemini(model_name, streaming, api_key)
else:
llm = self._create_openai(model_name, streaming, api_key)
llm = self._create_openai(model_name, streaming, api_key)
if json_mode:
llm = self._enable_json_mode(llm, model_name)
......@@ -95,34 +93,12 @@ class LLMFactory:
logger.error(f"❌ Failed to create model {model_name}: {e}")
raise
def _is_gemini_model(self, model_name: str) -> bool:
"""Check if model name is a Gemini model."""
return "gemini" in model_name.lower()
def _create_gemini(self, model_name: str, streaming: bool, api_key: str | None) -> BaseChatModel:
"""Create Gemini model instance."""
key = api_key or GOOGLE_API_KEY
if not key:
raise ValueError("GOOGLE_API_KEY is required for Gemini models")
llm = ChatGoogleGenerativeAI(
model=model_name,
streaming=streaming,
google_api_key=key,
temperature=0,
)
logger.info(f"✨ Created Gemini: {model_name}")
return llm
def _create_openai(self, model_name: str, streaming: bool, api_key: str | None) -> BaseChatModel:
"""Create OpenAI model instance with fallback to Gemini if needed."""
"""Create OpenAI model instance."""
key = api_key or OPENAI_API_KEY
if not key:
logger.warning("⚠️ No OpenAI key, attempting Gemini fallback")
if GOOGLE_API_KEY:
return self._create_gemini("gemini-1.5-flash", streaming, GOOGLE_API_KEY)
raise ValueError("Neither OPENAI_API_KEY nor GOOGLE_API_KEY is available")
raise ValueError("OPENAI_API_KEY is required")
llm = ChatOpenAI(
model=model_name,
......
......@@ -5,11 +5,11 @@ services:
container_name: canifa_backend
env_file: .env
ports:
- "8000:8000"
- "5000:5000"
volumes:
- .:/app
environment:
- PORT=8000
- PORT=5000
restart: unless-stopped
logging:
driver: "json-file"
......
......@@ -36,7 +36,7 @@ http://localhost:8000
```json
{
"user_id": "user_12345",
"user_query": "Cho em xem áo sơ mi nam dưới 500k"
"user_query": "Cho em xem áo sơ mi nam dưới 5a00k"
}
```
......
......@@ -11,25 +11,15 @@ Modules:
- conversation: Conversation history management
"""
import os
import logging
import os
# Configure Logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[logging.StreamHandler()]
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
from config import LANGSMITH_API_KEY, LANGSMITH_ENDPOINT, LANGSMITH_PROJECT, LANGSMITH_TRACING
# Ensure LangSmith Env Vars are set for the process
os.environ["LANGSMITH_TRACING"] = LANGSMITH_TRACING
os.environ["LANGSMITH_ENDPOINT"] = LANGSMITH_ENDPOINT
os.environ["LANGSMITH_API_KEY"] = LANGSMITH_API_KEY
os.environ["LANGSMITH_PROJECT"] = LANGSMITH_PROJECT
import uvicorn
from fastapi import FastAPI
......@@ -39,8 +29,6 @@ from fastapi.staticfiles import StaticFiles # Import StaticFiles
# Updated APIs
from api.chatbot_route import router as chatbot_router
from api.conservation_route import router as conservation_router
from common.middleware import ClerkAuthMiddleware
from config import PORT
app = FastAPI(
......@@ -49,13 +37,13 @@ app = FastAPI(
version="1.0.0",
)
# Clerk Auth (comment out if testing locally without valid token might be needed,
# Clerk Auth (comment out if testing locally without valid token might be needed,
# but user environment has it enabled. Static files usually should be public or protected.
# For now, we mount static files BEFORE auth if we want them public, or just let auth handle it.
# Usually static files for testing are convenient to be public
# Usually static files for testing are convenient to be public
# or we just need to authenticate via header in fetch.
# But for simple HTML test page, often it is easier if it is open.
# However, app.add_middleware applies to everything.
# However, app.add_middleware applies to everything.
# Let's assume ClerkAuthMiddleware allows public paths or we just login.
# IMPORTANT: If ClerkAuthMiddleware blocks everything, the static page won't load easily without token.
# But user just asked to mount it.
......@@ -76,9 +64,8 @@ app.add_middleware(
app.include_router(conservation_router)
# Chatbot Agent (Mới)
app.include_router(chatbot_router, prefix="/api/agent")
app.include_router(chatbot_router, prefix="/api/agent")
# Mount Static Files
# Mount this LAST to avoid conflicts with API routes
try:
static_dir = os.path.join(os.path.dirname(__file__), "static")
......@@ -93,6 +80,7 @@ except Exception as e:
async def root():
return {"message": "Contract AI Service is running!", "status": "healthy"}
if __name__ == "__main__":
print("=" * 60)
print("🚀 Contract AI Service Starting...")
......@@ -104,15 +92,13 @@ if __name__ == "__main__":
print("=" * 60)
# ENABLE_RELOAD = os.getenv("ENABLE_RELOAD", "false").lower() in ("true", "1", "yes")
ENABLE_RELOAD = True
print("⚠️ Hot reload: FORCED ON (Dev Mode)")
ENABLE_RELOAD = False # Tạm thời tắt reload để kiểm tra độ ổn định
print(f"⚠️ Hot reload: {ENABLE_RELOAD}")
reload_dirs = [
"ai_contract",
"conversation",
"common",
"api", # Watch api folder
"agent" # Watch agent folder
"api", # Watch api folder
"agent", # Watch agent folder
]
uvicorn.run(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment