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
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# Browser-safe (public key only, VITE_ prefix exposes to Vite client bundle)
VITE_LANGFUSE_PUBLIC_KEY=pk-lf-...
VITE_LANGFUSE_BASE_URL=https://langfuse.cofacts.tw
5 changes: 4 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ jobs:
SHA: ${{ github.sha }}
run: |
# Build frontend
docker build -t asia-east1-docker.pkg.dev/$PROJECT_ID/cofacts-ai/frontend:$SHA -f Dockerfile .
docker build \
--build-arg VITE_LANGFUSE_PUBLIC_KEY=${{ secrets.LANGFUSE_PUBLIC_KEY }} \
--build-arg VITE_LANGFUSE_BASE_URL="https://langfuse.cofacts.tw" \
-t asia-east1-docker.pkg.dev/$PROJECT_ID/cofacts-ai/frontend:$SHA -f Dockerfile .
docker push asia-east1-docker.pkg.dev/$PROJECT_ID/cofacts-ai/frontend:$SHA

# Build backend
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ RUN pnpm install --frozen-lockfile --ignore-scripts

# Copy source and build
COPY . .
ARG VITE_LANGFUSE_PUBLIC_KEY
ARG VITE_LANGFUSE_BASE_URL
RUN pnpm run build

# --- runtime ---
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,20 @@ pnpm install:agent
> source adk/.venv/bin/activate
> ```

3. Set up environment variables for the ADK agent:
3. Set up environment variables:

For the ADK backend agent:
```bash
cp adk/cofacts_ai/.env.example adk/cofacts_ai/.env
```

Edit `adk/cofacts_ai/.env` and fill in the required values (at minimum `GOOGLE_API_KEY`).

For the frontend UI (Vite / TanStack Start):
```bash
cp .env.example .env
```
Edit `.env` to configure your browser-safe variables (e.g. Langfuse keys).

4. Start the development server:

```bash
Expand Down
9 changes: 7 additions & 2 deletions adk/cofacts_ai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
submit_cofacts_reply,
resolve_vertex_redirect
)
from .instrumentation import setup_instrumentation
from google.adk.apps.app import App
from .instrumentation import setup_instrumentation, LangfuseTracingPlugin

load_dotenv()

Expand Down Expand Up @@ -545,5 +546,9 @@ async def append_grounding_sources(
],
)

root_agent = ai_writer
app = App(
name="cofacts_ai",
root_agent=ai_writer,
plugins=[LangfuseTracingPlugin()],
)

34 changes: 34 additions & 0 deletions adk/cofacts_ai/instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import logging
from openinference.instrumentation.google_adk import GoogleADKInstrumentor
from langfuse import get_client
from google.adk.plugins.base_plugin import BasePlugin
from google.adk.agents.invocation_context import InvocationContext
from google.adk.events.event import Event

logger = logging.getLogger(__name__)

Expand All @@ -20,3 +23,34 @@ def setup_instrumentation():
logger.info("Langfuse instrumentation initialized.")
else:
logger.warning("Langfuse authentication failed. Skipping instrumentation.")


class LangfuseTracingPlugin(BasePlugin):
"""
ADK Plugin that stamps each emitted event with the current Langfuse
trace ID in custom_metadata.

We use `before_run_callback` and `run_config.custom_metadata` because:
1. ADK's `Runner` merges `run_config.custom_metadata` into every event
generated during the invocation.
2. This merging happens *before* the event is persisted to the session
store.
3. Using `on_event_callback` would be too late for persistence, as ADK
saves the event to the database before the plugin's `on_event` is called.
"""

def __init__(self):
super().__init__(name="langfuse_tracing")

async def before_run_callback(
self, *, invocation_context: InvocationContext
):
langfuse = get_client()
trace_id = langfuse.get_current_trace_id()
if trace_id:
# Set the trace ID in run_config so ADK automatically stamps all
# future events in this invocation before they are saved to the DB.
if invocation_context.run_config.custom_metadata is None:
invocation_context.run_config.custom_metadata = {}
invocation_context.run_config.custom_metadata["langfuse_trace_id"] = trace_id
return None
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@tanstack/router-plugin": "^1.132.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"langfuse": "^3.38.20",
"lucide-react": "^0.574.0",
"nitro": "latest",
"openapi-fetch": "^0.17.0",
Expand Down
Loading
Loading