- shared/config.py: add LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, LANGFUSE_HOST - services/chat/app/tracing.py: new module — Langfuse client singleton, create_trace(), get_langfuse_callback(), get_prompt(), link_prompt_to_trace(), score_trace(), flush/shutdown helpers. Gracefully no-ops when keys are missing. - services/chat/app/llm.py: add callbacks param to get_llm() for LangChain callback handler injection - services/chat/app/deep_agent.py: accept langfuse_handler in all run_* and _run_single_agent* functions, pipe callbacks to LLM calls, fetch managed prompts from Langfuse with fallback to hardcoded system prompts - services/chat/app/redis_consumer.py: create Langfuse trace per request (home_request/floating_request), pass callback handler to deep_agent, link prompt name to trace, attach output preview, flush after each request - services/chat/app/main.py: shutdown Langfuse client in lifespan teardown - services/chat/requirements.txt: add langfuse>=2.0.0 Langfuse prompt names: 'home_system', 'floating_system' — create these in the Langfuse dashboard to manage prompts. Without them, hardcoded defaults are used transparently.
73 lines
2.0 KiB
Python
73 lines
2.0 KiB
Python
"""LLM factory — centralised model instantiation via LiteLLM.
|
|
|
|
Adapted from app/core/llm.py for the Chat Service.
|
|
Uses shared.config.settings instead of app.config.settings.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import warnings
|
|
|
|
from openai import AsyncOpenAI
|
|
import litellm
|
|
|
|
from langchain_openai import ChatOpenAI
|
|
from langchain_litellm import ChatLiteLLM
|
|
|
|
from shared.config import settings
|
|
|
|
litellm.drop_params = True
|
|
|
|
warnings.filterwarnings(
|
|
"ignore",
|
|
message=r"PydanticSerializationUnexpectedValue\(Expected `ResponseAPIUsage`",
|
|
category=UserWarning,
|
|
)
|
|
|
|
|
|
def _api_key_for_model(model: str) -> str | None:
|
|
if model.startswith("anthropic/"):
|
|
return settings.ANTHROPIC_API_KEY or None
|
|
if model.startswith("gemini/") or model.startswith("google/"):
|
|
return settings.GOOGLE_API_KEY or None
|
|
if model.startswith("cerebras/"):
|
|
return settings.CEREBRAS_API_KEY or None
|
|
if model.startswith("github_copilot/"):
|
|
return None
|
|
return settings.OPENAI_API_KEY or None
|
|
|
|
|
|
def get_llm(
|
|
*,
|
|
model: str | None = None,
|
|
temperature: float = 0,
|
|
callbacks: list | None = None,
|
|
) -> ChatOpenAI | ChatLiteLLM:
|
|
model = model or settings.LLM_MODEL
|
|
|
|
if settings.GITHUB_COPILOT_TOKEN_DIR:
|
|
os.environ.setdefault("GITHUB_COPILOT_TOKEN_DIR", settings.GITHUB_COPILOT_TOKEN_DIR)
|
|
|
|
if "/" in model:
|
|
return ChatLiteLLM(model=model, temperature=temperature, callbacks=callbacks)
|
|
|
|
return ChatOpenAI(
|
|
model=model,
|
|
temperature=temperature,
|
|
api_key=_api_key_for_model(model),
|
|
callbacks=callbacks,
|
|
)
|
|
|
|
|
|
async def embed(text: str) -> list[float]:
|
|
model = settings.LLM_EMBED_MODEL
|
|
|
|
if model.startswith("github_copilot/") or "/" in model:
|
|
response = await litellm.aembedding(model=model, input=[text])
|
|
return response.data[0]["embedding"]
|
|
|
|
client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
|
|
response = await client.embeddings.create(model=model, input=text)
|
|
return response.data[0].embedding
|