Skip to content

Commit 55da24b

Browse files
Merge pull request #30 from red-hat-data-services/upgrade-langfuse-v3
feat: Upgrade Langfuse v2 to v3
2 parents ca1d0df + 80da2f0 commit 55da24b

6 files changed

Lines changed: 170 additions & 25 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Agents are organized by framework. Pick one and follow its README:
2323
| **CrewAI** | [WebSearch Agent](./agents/crewai/websearch_agent/) | CrewAI-based agent with a web search tool to query the internet and answer user questions. |
2424
| **Vanilla Python** | [OpenAI Responses Agent](./agents/vanilla_python/openai_responses_agent/) | Minimal agent with no framework: only the OpenAI Python client and an Action/Observation loop with tools. Use with OpenAI or any compatible API. |
2525
| **AutoGen** | [MCP Agent](./agents/autogen/mcp_agent/) | AutoGen AssistantAgent with MCP tools over SSE (e.g. churn prediction, math tools), FastAPI `/chat/completions`. |
26+
| **Langflow** | [Simple Tool Calling Agent](./agents/langflow/simple_tool_calling_agent/) | Tool-calling agent built with Langflow's visual flow builder. Calls external APIs as tools and reasons over results. Includes Langfuse v3 tracing. Runs locally via `podman-compose`. |
2627

2728
## Deployment Options
2829

@@ -57,8 +58,10 @@ agentic-starter-kits/
5758
│ │ └── websearch_agent/ # LlamaIndex web search agent
5859
│ ├── vanilla_python/
5960
│ │ └── openai_responses_agent/ # OpenAI Responses API (no framework)
60-
│ └── autogen/
61-
│ └── mcp_agent/ # AutoGen + MCP (SSE)
61+
│ ├── autogen/
62+
│ │ └── mcp_agent/ # AutoGen + MCP (SSE)
63+
│ └── langflow/
64+
│ └── simple_tool_calling_agent/ # Langflow tool-calling agent
6265
└── README.md
6366
```
6467

agents/langflow/simple_tool_calling_agent/README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@
88

99
## What this agent does
1010

11-
Outdoor activity planning agent built with Langflow. It recommends the best day and time for outdoor activities by reasoning across weather forecasts, air quality, and National Park Service data.
11+
A tool-calling agent built with Langflow's visual flow builder. It calls external APIs as tools (weather forecasts, national park data) and reasons over the results to answer user questions. Includes Langfuse v3 tracing out of the box. Runs locally via `podman-compose`.
1212

13-
**Example query:** *"I want to go hiking near Denver this weekend. What day is best?"*
13+
### Included demo flow
14+
15+
The shipped flow is an outdoor activity assistant — it checks weather conditions and national park alerts to help decide if conditions are good for outdoor activities.
16+
17+
**Example queries:**
18+
- *"Can I go walking in Boston tomorrow at 3 PM?"*
19+
- *"I want to go hiking near Denver this weekend. What day is best?"*
20+
- *"Is it a good day for a picnic in San Francisco?"*
1421

1522
### Tools
1623
| Tool | API | Description |
@@ -52,9 +59,9 @@ chmod +x deploy-local.sh cleanup-local.sh
5259

5360
This starts:
5461
- **Langflow** on http://localhost:7860 — the agent UI
55-
- **PostgreSQL** — shared database server. Hosts two databases: `langflow` (flows, users, settings) and `langfuse` (traces). The `langflow` database is created automatically by PostgreSQL; the `langfuse` database is created by `local/init-db.sh` on first startup
62+
- **PostgreSQL** — shared database server. Hosts two databases: `langflow` (flows, users, settings) and `langfuse` (metadata). The `langflow` database is created automatically by PostgreSQL; the `langfuse` database is created by `local/init-db.sh` on first startup
5663
- **Ollama** on http://localhost:11434 — local LLM (qwen2.5:7b), runs natively on host for GPU acceleration
57-
- **Langfuse v2** on http://localhost:3000 — tracing (admin@langflow.local / admin123)
64+
- **Langfuse v3** on http://localhost:3000 — tracing (admin@langflow.local / password auto-generated in `local/.env`), backed by ClickHouse, MinIO, and Redis
5865

5966
### Import and configure the flow
6067

@@ -102,8 +109,9 @@ On the cluster, replace `localhost:7860` with your cluster's Langflow route URL.
102109
|------|---------------------|----------------------------|
103110
| Downloaded Ollama models (e.g., qwen2.5:7b) | Kept | Kept (stored on host, not in containers) |
104111
| Imported Langflow flows | Kept | **Deleted** (re-import needed) |
105-
| Langfuse traces | Kept | **Deleted** |
112+
| Langfuse traces (ClickHouse + MinIO) | Kept | **Deleted** |
106113
| PostgreSQL data | Kept | **Deleted** |
114+
| Redis cache | Kept | **Deleted** |
107115
| `.env` and `.ollama-enabled` config | Kept | **Deleted** |
108116

109117
---
@@ -202,7 +210,7 @@ Then update the **KServe vLLM** component in the Langflow UI to point to your re
202210
After running the agent, traces are automatically sent to Langfuse.
203211

204212
**Locally:**
205-
1. Open http://localhost:3000 (login: admin@langflow.local / admin123)
213+
1. Open http://localhost:3000 (login: admin@langflow.local / password auto-generated in `local/.env`)
206214
2. Select the **Langflow Agent** project
207215
3. Click **Traces** in the left sidebar
208216
4. Click on any trace to see the full agent execution — LLM calls, tool invocations, inputs, and outputs

agents/langflow/simple_tool_calling_agent/deploy-local.sh

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,22 @@ fi
2323
# Copy .env if it doesn't exist
2424
if [ ! -f "$LOCAL_DIR/.env" ]; then
2525
cp "$LOCAL_DIR/.env.example" "$LOCAL_DIR/.env"
26-
echo "Created .env from .env.example — edit it if needed."
26+
echo "Created .env from .env.example"
27+
fi
28+
29+
# Generate Langfuse secrets if not already set
30+
if ! grep -q '^LANGFUSE_ADMIN_PASSWORD=.\+' "$LOCAL_DIR/.env" 2>/dev/null; then
31+
GENERATED_PWD=$(openssl rand -base64 12)
32+
sed -i.bak "s/^LANGFUSE_ADMIN_PASSWORD=.*/LANGFUSE_ADMIN_PASSWORD=$GENERATED_PWD/" "$LOCAL_DIR/.env"
33+
rm -f "$LOCAL_DIR/.env.bak"
34+
echo "Generated Langfuse admin password."
35+
fi
36+
37+
if ! grep -q '^LANGFUSE_ENCRYPTION_KEY=.\+' "$LOCAL_DIR/.env" 2>/dev/null; then
38+
GENERATED_KEY=$(openssl rand -hex 32)
39+
sed -i.bak "s/^LANGFUSE_ENCRYPTION_KEY=.*/LANGFUSE_ENCRYPTION_KEY=$GENERATED_KEY/" "$LOCAL_DIR/.env"
40+
rm -f "$LOCAL_DIR/.env.bak"
41+
echo "Generated Langfuse encryption key."
2742
fi
2843

2944
# Ask about Ollama on first run
@@ -81,11 +96,11 @@ cd "$LOCAL_DIR" || { echo "ERROR: Directory $LOCAL_DIR not found."; exit 1; }
8196

8297
# Start containerized services (Langflow, PostgreSQL, Langfuse)
8398
echo ""
84-
echo "Starting local stack (Langflow + PostgreSQL + Langfuse)..."
99+
echo "Starting local stack (Langflow + PostgreSQL + Langfuse v3 + ClickHouse + MinIO + Redis)..."
85100
podman-compose up -d
86101

87102
echo ""
88-
echo "Waiting for Langflow to start (this may take a minute)..."
103+
echo "Waiting for Langflow to start (this may take a few minutes)..."
89104
LANGFLOW_READY=false
90105
for i in $(seq 1 60); do
91106
if curl -s http://localhost:7860/health >/dev/null 2>&1; then
@@ -102,11 +117,28 @@ if [ "$LANGFLOW_READY" = false ]; then
102117
exit 1
103118
fi
104119

120+
echo ""
121+
echo "Waiting for Langfuse to start (this may take several minutes)..."
122+
LANGFUSE_READY=false
123+
for i in $(seq 1 60); do
124+
if curl -s http://localhost:3000/api/public/health >/dev/null 2>&1; then
125+
LANGFUSE_READY=true
126+
echo "Langfuse is ready!"
127+
break
128+
fi
129+
sleep 10
130+
done
131+
132+
if [ "$LANGFUSE_READY" = false ]; then
133+
echo "WARNING: Langfuse did not start within 10 minutes. Tracing and logging will not work until it is up."
134+
echo "Check logs: podman logs local_langfuse-web_1"
135+
fi
136+
105137
echo ""
106138
echo "=== Local environment is ready ==="
107139
echo ""
108140
echo " Langflow UI: http://localhost:7860"
109-
echo " Langfuse: http://localhost:3000 (login: admin@langflow.local / admin123)"
141+
echo " Langfuse: http://localhost:3000 (login: admin@langflow.local / password auto-generated in local/.env)"
110142
if [ "$USE_OLLAMA" = "yes" ]; then
111143
echo " Ollama API: http://localhost:11434 (running natively on host)"
112144
fi

agents/langflow/simple_tool_calling_agent/flows/outdoor-activity-agent.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@
995995
"trace_as_input": true,
996996
"trace_as_metadata": true,
997997
"type": "str",
998-
"value": "You are a national park trip planner and weather assistant. You MUST use the available tools to get real data for every question - never guess or make up information about alerts, weather, or park details.\n\nIMPORTANT: For every question about a park, call the appropriate tool. Do not answer from memory.\n\nTools:\nNPS Search Parks \u2014 find parks by state code and activity keyword\nNPS Park Alerts \u2014 get active closures/hazards for a park (uses four-letter park code)\nOpen-Meteo Forecast \u2014 get weather forecast for coordinates (latitude, longitude)\n\nWorkflow:\n\nParse the request. Identify: state(s), activity/interest, travel dates (if given), and group needs (e.g., kids, accessibility).\n\nSearch parks. Call NPS Search Parks with the state code and activity. Extract the park code, coordinates, and description from results. If the user mentions a specific park by name, still search to confirm its park code and coordinates.\n\nCheck alerts for each candidate park. Call NPS Park Alerts using the park code. Classify alerts as:\n\nBlocking (closure, danger): disqualifies the park unless the closure is partial and doesn't affect the user's activity.\nInformational (caution, info): mention but don't disqualify.\nCheck weather. Call Open-Meteo Forecast using the park's latitude and longitude. Evaluate conditions against the user's planned activity \u2014 e.g., rain matters more for hiking than museum visits. If the user provided travel dates, focus on that date range.\n\nRecommend. Pick the best park considering all three factors. If the top choice has blocking alerts or severe weather, suggest an alternative from the search results.\n\nShortcuts:\n\nWeather-only question \u2192 skip NPS tools, forecast directly.\nAlert-only question \u2192 skip weather tool.\nUser names a specific park \u2192 still check alerts and weather, skip search only if you already have the park code and coordinates.\n\nResponse format:\n\nProvide your recommendation in this structure:\n\nPark: name, location, and why it fits the activity\n\nAlerts: any active alerts (or \"No active alerts\")\n\nWeather Outlook: conditions for the travel dates, with a note on suitability for the activity\n\nTips: 1-2 practical tips (best trails, gear, timing, alternatives if weather turns)"
998+
"value": "You are an outdoor activity assistant. You help people decide whether conditions are good for outdoor activities — walking, hiking, cycling, picnics, park visits, or anything else outside. You MUST use the available tools to get real data — never guess or make up weather, alerts, or park details.\n\nIMPORTANT: Always call the appropriate tool. Do not answer from memory.\n\nTools:\nOpen-Meteo Forecast — get weather forecast for coordinates (latitude, longitude)\nNPS Search Parks — find national parks by state code and activity keyword\nNPS Park Alerts — get active closures/hazards for a park (uses four-letter park code)\n\nHow to decide which tools to use:\n\n1. Every question gets a weather check. Look up the coordinates for the location the user mentioned (city, park, area) and call Open-Meteo Forecast.\n\n2. Only use NPS tools when the user asks about national parks, hiking trails, or outdoor activities near nature areas. If someone asks about walking in a city, cycling to work, or having a picnic in a city park, skip the NPS tools — they only cover national parks.\n\n3. When you do use NPS tools: search for parks first, then check alerts for each candidate. Classify alerts as blocking (closure, danger) or informational (caution, info).\n\nWorkflow:\n\nParse the request. Identify: location, activity, and dates/times (if given).\n\nCheck weather. Call Open-Meteo Forecast for the location. Evaluate conditions for the specific activity — rain matters more for a picnic than for a museum visit, wind matters for cycling, UV matters for a beach day.\n\nIf relevant, search parks. Only if the user is asking about hiking, national parks, or nature activities near a region. Call NPS Search Parks, then NPS Park Alerts for candidates.\n\nGive a clear answer. Lead with a direct yes/no/maybe recommendation, then explain why based on the data.\n\nResponse format:\n\nAdapt your response to the question. For simple weather checks, keep it short and direct. For park trip planning, include more detail. Always include:\n\n- A clear recommendation (good day for it, not ideal, or avoid)\n- Weather conditions that matter for the activity\n- Alerts or park info only if NPS tools were used\n- 1-2 practical tips if helpful"
999999
},
10001000
"tools": {
10011001
"_input_type": "HandleInput",

agents/langflow/simple_tool_calling_agent/local/.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ POSTGRES_USER=langflow
33
POSTGRES_PASSWORD=langflow
44
POSTGRES_DB=langflow
55

6+
# Langfuse — admin password and encryption key are auto-generated on first run by deploy-local.sh
7+
LANGFUSE_ADMIN_PASSWORD=
8+
LANGFUSE_ENCRYPTION_KEY=
9+
610
# Ollama — model to pull on first run
711
OLLAMA_MODEL=qwen2.5:7b
8-

agents/langflow/simple_tool_calling_agent/local/podman-compose.yml

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ services:
1515
OPENAI_API_KEY: "not-needed"
1616
OLLAMA_BASE_URL: "http://host.containers.internal:11434"
1717
# Langfuse tracing
18-
LANGFUSE_HOST: "http://langfuse:3000"
18+
LANGFUSE_HOST: "http://langfuse-web:3000"
1919
LANGFUSE_PUBLIC_KEY: "pk-lf-local-dev"
2020
LANGFUSE_SECRET_KEY: "sk-lf-local-dev"
2121
volumes:
2222
- langflow_data:/var/lib/langflow:Z
2323
depends_on:
2424
postgres:
2525
condition: service_healthy
26-
langfuse:
26+
langfuse-web:
2727
condition: service_started
2828

2929
# ── PostgreSQL (shared by Langflow and Langfuse) ────────────────────
@@ -42,16 +42,54 @@ services:
4242
timeout: 5s
4343
retries: 5
4444

45-
# ── Langfuse v2 (Tracing) ──────────────────────────────────────────
46-
langfuse:
47-
image: docker.io/langfuse/langfuse:2
45+
# ── Langfuse v3 (Tracing) ────────────────────────────────────────
46+
47+
langfuse-web:
48+
image: docker.io/langfuse/langfuse:3.162.0
49+
restart: always
4850
ports:
49-
- "3000:3000"
50-
environment:
51+
- "127.0.0.1:3000:3000"
52+
depends_on: &langfuse-depends-on
53+
postgres:
54+
condition: service_healthy
55+
clickhouse:
56+
condition: service_healthy
57+
minio:
58+
condition: service_healthy
59+
redis:
60+
condition: service_healthy
61+
environment: &langfuse-env
5162
DATABASE_URL: "postgresql://${POSTGRES_USER:-langflow}:${POSTGRES_PASSWORD:-langflow}@postgres:5432/langfuse"
5263
NEXTAUTH_URL: "http://localhost:3000"
5364
NEXTAUTH_SECRET: "local-dev-secret"
5465
SALT: "local-dev-salt"
66+
ENCRYPTION_KEY: "${LANGFUSE_ENCRYPTION_KEY}"
67+
TELEMETRY_ENABLED: "false"
68+
# ClickHouse
69+
CLICKHOUSE_MIGRATION_URL: "clickhouse://clickhouse:9000"
70+
CLICKHOUSE_URL: "http://clickhouse:8123"
71+
CLICKHOUSE_USER: "clickhouse"
72+
CLICKHOUSE_PASSWORD: "clickhouse"
73+
CLICKHOUSE_CLUSTER_ENABLED: "false"
74+
# Redis
75+
REDIS_HOST: "redis"
76+
REDIS_PORT: "6379"
77+
REDIS_AUTH: "localredis"
78+
# MinIO (S3-compatible object storage)
79+
LANGFUSE_S3_EVENT_UPLOAD_BUCKET: "langfuse"
80+
LANGFUSE_S3_EVENT_UPLOAD_REGION: "auto"
81+
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: "minio"
82+
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: "miniosecret"
83+
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: "http://minio:9000"
84+
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true"
85+
LANGFUSE_S3_EVENT_UPLOAD_PREFIX: "events/"
86+
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: "langfuse"
87+
LANGFUSE_S3_MEDIA_UPLOAD_REGION: "auto"
88+
LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: "minio"
89+
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: "miniosecret"
90+
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: "http://minio:9000"
91+
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: "true"
92+
LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: "media/"
5593
# Headless init — auto-creates org, project, user, and API keys
5694
LANGFUSE_INIT_ORG_ID: "langflow-org"
5795
LANGFUSE_INIT_ORG_NAME: "Langflow Agent"
@@ -61,11 +99,72 @@ services:
6199
LANGFUSE_INIT_PROJECT_SECRET_KEY: "sk-lf-local-dev"
62100
LANGFUSE_INIT_USER_EMAIL: "admin@langflow.local"
63101
LANGFUSE_INIT_USER_NAME: "admin"
64-
LANGFUSE_INIT_USER_PASSWORD: "admin123"
65-
depends_on:
66-
postgres:
67-
condition: service_healthy
102+
LANGFUSE_INIT_USER_PASSWORD: "${LANGFUSE_ADMIN_PASSWORD}"
103+
104+
langfuse-worker:
105+
image: docker.io/langfuse/langfuse-worker:3.162.0
106+
restart: always
107+
depends_on: *langfuse-depends-on
108+
environment:
109+
<<: *langfuse-env
110+
111+
# ── ClickHouse (Langfuse analytics DB) ──────────────────────────────
112+
clickhouse:
113+
image: docker.io/clickhouse/clickhouse-server:26.2
114+
restart: always
115+
user: "101:101"
116+
environment:
117+
CLICKHOUSE_DB: default
118+
CLICKHOUSE_USER: clickhouse
119+
CLICKHOUSE_PASSWORD: clickhouse
120+
volumes:
121+
- clickhouse_data:/var/lib/clickhouse:Z
122+
- clickhouse_logs:/var/log/clickhouse-server:Z
123+
healthcheck:
124+
test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1
125+
interval: 5s
126+
timeout: 5s
127+
retries: 10
128+
start_period: 30s
129+
130+
# ── MinIO (Langfuse S3-compatible object storage) ───────────────────
131+
minio:
132+
# MinIO uses date-based release tags instead of semver
133+
image: docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z
134+
restart: always
135+
entrypoint: sh
136+
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
137+
environment:
138+
MINIO_ROOT_USER: minio
139+
MINIO_ROOT_PASSWORD: miniosecret
140+
volumes:
141+
- minio_data:/data:Z
142+
healthcheck:
143+
test: ["CMD", "mc", "ready", "local"]
144+
interval: 5s
145+
timeout: 5s
146+
retries: 5
147+
start_period: 30s
148+
149+
# ── Redis (Langfuse caching + job queue) ────────────────────────────
150+
redis:
151+
image: docker.io/redis:7.4.8
152+
restart: always
153+
command: >
154+
--requirepass localredis
155+
--maxmemory-policy noeviction
156+
volumes:
157+
- redis_data:/data:Z
158+
healthcheck:
159+
test: ["CMD", "redis-cli", "-a", "localredis", "ping"]
160+
interval: 3s
161+
timeout: 10s
162+
retries: 10
68163

69164
volumes:
70165
langflow_data:
71166
postgres_data:
167+
clickhouse_data:
168+
clickhouse_logs:
169+
minio_data:
170+
redis_data:

0 commit comments

Comments
 (0)