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
6 changes: 6 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ LLM_QUEST_CLAUDE_EXEC_PATH=claude
LLM_QUEST_CLAUDE_EXEC_MODEL=
LLM_QUEST_CLAUDE_EXEC_ARGS=

# Langfuse tracing (optional). When set, all LLM calls are traced automatically.
# Sign up at https://cloud.langfuse.com or self-host.
LANGFUSE_SECRET_KEY=
LANGFUSE_PUBLIC_KEY=
LANGFUSE_BASE_URL=https://cloud.langfuse.com

# Optional token pricing overrides (USD per 1M tokens) used for run cost estimates.
# If omitted, built-in defaults are used for known models.
# Format:
Expand Down
2 changes: 2 additions & 0 deletions llm_quest_benchmark/executors/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from llm_quest_benchmark.core.logging import DEFAULT_DB_PATH
from llm_quest_benchmark.core.runner import run_quest_with_timeout
from llm_quest_benchmark.environments.state import QuestOutcome
from llm_quest_benchmark.llm import tracing
from llm_quest_benchmark.schemas.config import BenchmarkConfig

# Configure logging
Expand Down Expand Up @@ -304,6 +305,7 @@ def run_benchmark(config: BenchmarkConfig, progress_callback=None) -> list[dict[
if artifact_dir:
logger.info("Benchmark artifacts saved to %s", artifact_dir)

tracing.flush()
return results


Expand Down
3 changes: 3 additions & 0 deletions llm_quest_benchmark/executors/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
print_summary,
run_benchmark,
)
from llm_quest_benchmark.llm import tracing
from llm_quest_benchmark.renderers.terminal import RichRenderer
from llm_quest_benchmark.schemas.config import AgentConfig, BenchmarkConfig

Expand Down Expand Up @@ -433,6 +434,8 @@ def close_callback(event, data):
except Exception as e:
log.exception(f"Error during quest run: {e}")
raise typer.Exit(code=2)
finally:
tracing.flush()


@app.command()
Expand Down
28 changes: 21 additions & 7 deletions llm_quest_benchmark/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,35 @@

import anthropic
from dotenv import load_dotenv
from openai import OpenAI

from llm_quest_benchmark.constants import (
# Load .env BEFORE checking Langfuse (needs LANGFUSE_SECRET_KEY from .env).
load_dotenv(dotenv_path=Path(__file__).resolve().parents[2] / ".env", override=False)

from llm_quest_benchmark.llm.tracing import is_enabled as _langfuse_enabled # noqa: E402

if _langfuse_enabled():
from langfuse import observe # noqa: E402
from langfuse.openai import OpenAI # noqa: E402
else:
from openai import OpenAI # noqa: E402

def observe(**_kwargs): # type: ignore[misc]
def _noop(fn):
return fn

return _noop


from llm_quest_benchmark.constants import ( # noqa: E402
DEFAULT_TEMPERATURE,
MODEL_ALIASES,
MODEL_PROVIDER_CONFIG,
)
from llm_quest_benchmark.llm.cost import UsageStats, estimate_cost_usd
from llm_quest_benchmark.llm.cost import UsageStats, estimate_cost_usd # noqa: E402

logger = logging.getLogger(__name__)
# Configure httpx logger to only show in debug mode
logging.getLogger("httpx").setLevel(logging.WARNING)

# Load local .env when running directly from repository root.
load_dotenv(dotenv_path=Path(__file__).resolve().parents[2] / ".env", override=False)


@dataclass(frozen=True)
class ModelSpec:
Expand Down Expand Up @@ -385,6 +398,7 @@ def _get_client(self) -> anthropic.Anthropic:
self._client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
return self._client

@observe(as_type="generation", name="anthropic-completion")
def get_completion(self, prompt: str) -> str:
"""Get a completion from the model."""

Expand Down
23 changes: 23 additions & 0 deletions llm_quest_benchmark/llm/tracing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Langfuse tracing integration. Activates when LANGFUSE_SECRET_KEY is set."""

import logging
import os
from functools import lru_cache

logger = logging.getLogger(__name__)


@lru_cache(maxsize=1)
def is_enabled() -> bool:
return bool(os.getenv("LANGFUSE_SECRET_KEY"))


def flush() -> None:
if not is_enabled():
return
try:
from langfuse import Langfuse

Langfuse().flush()
except Exception:
logger.debug("Langfuse flush failed", exc_info=True)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"openai>=1.12.0",
"openrouter>=0.5.0",
"typer>=0.9.0",
"langfuse>=3.0.0",
]

[project.optional-dependencies]
Expand Down
Loading
Loading