Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ TZ=Europe/Madrid
# Get your API key from: https://platform.openai.com/api-keys
OPENAI_API_KEY=your_openai_api_key_here

# ============================================
# LANGFUSE OBSERVABILITY
# ============================================

# Langfuse credentials for LLM tracing. Get keys from Langfuse UI -> Settings -> API Keys.
# Leave blank to disable Langfuse tracing entirely.
# Regions: EU=https://cloud.langfuse.com, US=https://us.cloud.langfuse.com
LANGFUSE_PUBLIC_KEY=
LANGFUSE_SECRET_KEY=
LANGFUSE_BASE_URL=

# ============================================
# HUGGING FACE HUB (BUILD-TIME ONLY)
# ============================================
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ cp .env.example .env
| `LOGTO_MANAGEMENT_API_RESOURCE` | Resource opcional de la Management API de Logto |
| `LOGTO_DEFAULT_USER_ROLE_ID` | Rol global opcional para auto-asignación de usuarios |

#### Langfuse (Trazabilidad)

Tracing opcional de las llamadas LLM y del grafo de LangGraph. Si las tres variables están vacías, el backend arranca con el tracing desactivado y no envía datos a Langfuse.

| Variable | Descripción | Ejemplo |
| --- | --- | --- |
| `LANGFUSE_PUBLIC_KEY` | Clave pública del proyecto (Langfuse UI → Settings → API Keys) | `pk-lf-...` |
| `LANGFUSE_SECRET_KEY` | Clave secreta del proyecto | `sk-lf-...` |
| `LANGFUSE_BASE_URL` | Host de Langfuse. | |

#### Aplicación

| Variable | Descripción | Default |
Expand Down
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ dependencies = [
"langchain-core==1.2.18",
"langchain-openai==1.1.11",
"langchain-text-splitters==1.1.1",
"langfuse>=4.0.0,<5.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify langfuse package version availability on PyPI
echo "Checking langfuse versions on PyPI..."
curl -s https://pypi.org/pypi/langfuse/json | jq -r '.releases | keys[]' | grep '^4\.' | head -20

Repository: in2ai/ASM2-client

Length of output: 218


🌐 Web query:

Does langfuse 4.0.0 or higher exist? What is the correct parameter name for the Langfuse constructor to set the base URL/host in version 4.x?

💡 Result:

Yes, Langfuse Python SDK version 4.0.0 and higher exist. The latest version is 4.6.1 as of May 2026.[1][2] In version 4.x, the correct parameter name for the Langfuse constructor to set the base URL/host is base_url. The host parameter is deprecated; use base_url instead.[3][2][4][5][6]

Citations:


🏁 Script executed:

cat -n backend/src/tracing/langfuse_setup.py

Repository: in2ai/ASM2-client

Length of output: 1191


Replace host parameter with base_url in Langfuse constructor call.

Langfuse version 4.0.0 exists and is available on PyPI. However, the code at line 24 uses the deprecated host parameter when instantiating Langfuse(). In version 4.x, the correct parameter is base_url. Change host=base_url to base_url=base_url to ensure compatibility with the pinned version constraint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/pyproject.toml` at line 66, The Langfuse client is being constructed
using the deprecated host parameter; update the Langfuse(...) instantiation
(look for the call to Langfuse in the codebase) to replace host=base_url with
base_url=base_url so it matches langfuse>=4.0.0's API; ensure any variable name
(base_url) is unchanged and run tests/imports to confirm the new parameter is
accepted.

"langgraph==1.1.2",
"langsmith==0.4.46",
"logto==0.2.1",
Expand Down
14 changes: 13 additions & 1 deletion backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
top_k_topics,
)
from src.chat.store import ChatNotFoundError, ChatStore
from src.tracing import get_langfuse_handler
from src.utils.nlp import init_nlp
from src.utils.rag import get_reranker, retrieve_and_rerank

Expand Down Expand Up @@ -428,7 +429,7 @@ async def _run_chat_turn(auth: AuthInfo, chat_id: str, query: str) -> dict[str,

register_user_activity(questdb_pool)

config = {
config: dict[str, Any] = {
"configurable": {
"thread_id": chat_id,
"vectorstore": app.state.vectorstore,
Expand All @@ -438,6 +439,17 @@ async def _run_chat_turn(auth: AuthInfo, chat_id: str, query: str) -> dict[str,
}
}

langfuse_handler = get_langfuse_handler()

if langfuse_handler is not None:
config["callbacks"] = [langfuse_handler]
config["run_name"] = "chat-turn"
config["metadata"] = {
"langfuse_user_id": auth.sub,
"langfuse_session_id": chat_id,
"langfuse_tags": ["asm2", "chat"],
}

with TimedMetric(questdb_pool, Metrics.LLM_RESPONSE_TIME.value):
try:
result = await app.state.graph.ainvoke(
Expand Down
3 changes: 3 additions & 0 deletions backend/src/tracing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .langfuse_setup import get_langfuse_handler

__all__ = ["get_langfuse_handler"]
32 changes: 32 additions & 0 deletions backend/src/tracing/langfuse_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import logging
import os
from functools import lru_cache
from typing import Any


@lru_cache(maxsize=1)
def get_langfuse_handler() -> Any | None:
public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
secret_key = os.getenv("LANGFUSE_SECRET_KEY")
base_url = os.getenv("LANGFUSE_BASE_URL")

if not public_key or not secret_key or not base_url:
logging.info("Langfuse credentials not set; tracing disabled")
return None

try:
from langfuse import Langfuse
from langfuse.langchain import CallbackHandler

Langfuse(
public_key=public_key,
secret_key=secret_key,
host=base_url,
)
logging.info("Langfuse tracing enabled (host=%s)", base_url)

return CallbackHandler()

except Exception:
logging.warning("Failed to initialize Langfuse; tracing disabled", exc_info=True)
return None
Loading