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 agents/langflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Langflow Agents

Agent templates built with [Langflow](https://docs.langflow.org/)'s visual flow builder.

> **Note:** Langflow agents are different from other agents in this repo. They do not deploy a custom container — the "agent" is a JSON flow definition imported into an existing Langflow instance. See each agent's README for details.

## Available Agents

| Agent | Description |
|-------|-------------|
| [Simple Tool Calling Agent](simple_tool_calling_agent/) | Tool-calling agent that calls external APIs (weather, parks) and reasons over results. Includes Langfuse v3 tracing. |
8 changes: 8 additions & 0 deletions agents/langflow/simple_tool_calling_agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# PostgreSQL
POSTGRES_USER=langflow
POSTGRES_PASSWORD=langflow
POSTGRES_DB=langflow

# Langfuse — admin password and encryption key are auto-generated on first run by make init
LANGFUSE_ADMIN_PASSWORD=
LANGFUSE_ENCRYPTION_KEY=
42 changes: 42 additions & 0 deletions agents/langflow/simple_tool_calling_agent/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
SHELL := bash
COMPOSE_FILE := local/podman-compose.yml
ENV_FILE := local/.env

.PHONY: init run stop clean status logs help

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}'

init: ## Create local/.env from .env.example and generate secrets
@if [ ! -f $(ENV_FILE) ]; then \
cp .env.example $(ENV_FILE) && echo "Created local/.env from .env.example"; \
else \
echo "local/.env already exists — skipping"; \
fi
@if ! grep -q '^LANGFUSE_ADMIN_PASSWORD=.\+' $(ENV_FILE) 2>/dev/null; then \
GENERATED_PWD=$$(openssl rand -base64 12) && \
sed -i.bak "s/^LANGFUSE_ADMIN_PASSWORD=.*/LANGFUSE_ADMIN_PASSWORD=$$GENERATED_PWD/" $(ENV_FILE) && \
rm -f $(ENV_FILE).bak && \
echo "Generated Langfuse admin password."; \
fi
Comment on lines +16 to +21

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.

This is also present in deploy-local.sh. can be removed from one place

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good catch, removed it from deploy-local.sh

@if ! grep -q '^LANGFUSE_ENCRYPTION_KEY=.\+' $(ENV_FILE) 2>/dev/null; then \
GENERATED_KEY=$$(openssl rand -hex 32) && \
sed -i.bak "s/^LANGFUSE_ENCRYPTION_KEY=.*/LANGFUSE_ENCRYPTION_KEY=$$GENERATED_KEY/" $(ENV_FILE) && \
rm -f $(ENV_FILE).bak && \
echo "Generated Langfuse encryption key."; \
fi

run: init ## Start the local Langflow stack (Langflow + PostgreSQL + Langfuse)
@./deploy-local.sh

stop: ## Stop services (data preserved)
@./cleanup-local.sh

clean: ## Stop services and remove all data
@./cleanup-local.sh --force

status: ## Show status of running services
@podman-compose -f $(COMPOSE_FILE) ps

logs: ## Tail logs from all services
@podman-compose -f $(COMPOSE_FILE) logs -f
216 changes: 84 additions & 132 deletions agents/langflow/simple_tool_calling_agent/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
<div style="text-align: center;">

# Langflow Simple Tool Calling Agent
# Simple Tool Calling Agent

</div>

---

## What this agent does

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`.

### Included demo flow

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.
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.

**Example queries:**
- *"Can I go walking in Boston tomorrow at 3 PM?"*
Expand All @@ -26,103 +22,115 @@ The shipped flow is an outdoor activity assistant — it checks weather conditio
| NPS Search Parks | NPS API | Search national parks by state |
| NPS Park Alerts | NPS API | Active alerts and closures for a park |

> **Note:** Unlike other agents in this repo, Langflow agents do not deploy a custom container. The "agent" is a JSON flow definition imported into an existing Langflow instance. There is no Dockerfile, Helm chart, or FastAPI application.

---

## Run locally
## Prerequisites

### Prerequisites
- [Podman](https://podman.io/) + [podman-compose](https://github.com/containers/podman-compose) — for running the local stack
- [GNU Make](https://www.gnu.org/software/make/) and a bash-compatible shell — on Windows, use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) (recommended) or [Git Bash](https://git-scm.com/downloads)

- **Podman** + **podman-compose** installed
### Installing Podman

**macOS:**
```bash
brew install podman # install podman runtime
brew install podman-compose # install compose plugin
podman machine init # create a Linux VM (podman runs containers in a VM on macOS)
podman machine start # start the VM
```
**macOS:**
```bash
brew install podman # install podman runtime
brew install podman-compose # install compose plugin
podman machine init # create a Linux VM (podman runs containers in a VM on macOS)
podman machine start # start the VM
```

**Linux:**
```bash
sudo dnf install -y podman # install podman runtime
pip install podman-compose # install compose plugin
sudo systemctl start podman # start the podman service
```
**Linux:**
```bash
sudo dnf install -y podman # install podman runtime
pip install podman-compose # install compose plugin
sudo systemctl start podman # start the podman service
```

### Start the local stack
## Deploying Locally

### Setup

```bash
cd agents/langflow/simple_tool_calling_agent
chmod +x deploy-local.sh cleanup-local.sh
./deploy-local.sh
make init # creates local/.env from .env.example, generates Langfuse secrets
make run # starts Langflow + PostgreSQL + Langfuse v3 (ClickHouse, MinIO, Redis)
```

This starts:
- **Langflow** on http://localhost:7860 — the agent UI
- **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
- **Ollama** on http://localhost:11434 — local LLM (qwen2.5:7b), runs natively on host for GPU acceleration
- **Langfuse v3** on http://localhost:3000 — tracing (admin@langflow.local / password auto-generated in `local/.env`), backed by ClickHouse, MinIO, and Redis

### Import and configure the flow
### Import the flow

1. Open http://localhost:7860
2. On first launch, Langflow asks you to create a flow — create a **Blank Flow** (this is just to get past the initial screen)
3. Click the **Langflow icon** (top left) to go to the projects page
4. Click **Upload Flow** and select `flows/outdoor-activity-agent.json`
5. Configure the flow components:

| Component | Field | Value |
|-----------|-------|-------|
| KServe vLLM | api_base | http://host.containers.internal:11434/v1 |
| KServe vLLM | model_name | qwen2.5:7b |
| KServe vLLM | api_key | dummy |
| NPS Search Parks | api_key | Get one free at https://developer.nps.gov |
| NPS Park Alerts | api_key | Same NPS key as above |
6. Run the agent from the Langflow UI
### Configuration

> **Note:** Ollama runs natively on your machine (not in a container) to leverage GPU acceleration. On Apple Silicon Macs, this uses Metal for faster inference. Response times vary by hardware — expect 30 seconds to a few minutes per tool call. For faster responses, point the agent to a GPU-backed cluster endpoint (see [Remote model](#remote-model-instead-of-ollama)).
Configure the flow components:

### Query the agent via API
| Component | Field | Value |
|-----------|-------|-------|
| KServe vLLM | api_base | http://host.containers.internal:8321/v1 |
| KServe vLLM | model_name | ollama/qwen2.5:7b |
| KServe vLLM | api_key | not-needed |
| NPS Search Parks | api_key | Get one free at https://developer.nps.gov |
| NPS Park Alerts | api_key | Same NPS key as above |

Once the flow is running, you can query it via the Langflow API:
#### Pointing to a locally hosted model

```bash
curl -X POST http://localhost:7860/api/v1/run/<flow-id> \
-H "Content-Type: application/json" \
-d '{"input_value": "What is the best day to hike near Denver?", "output_type": "chat", "input_type": "chat"}'
```
See [Local Development](../../../docs/local-development.md) for Ollama + Llama Stack setup for local model serving.

Replace `<flow-id>` with the flow ID from the Langflow UI. You can find it in the browser URL bar when you open the flow — e.g., `http://localhost:7860/flow/27e7203d-b2b1-4700-962a-144a66155f14` → the flow ID is `27e7203d-b2b1-4700-962a-144a66155f14`.
**Notes:**
- `api_base` — use `host.containers.internal` instead of `localhost` so containerized Langflow can reach Llama Stack running on the host
- `api_key` — Llama Stack doesn't require authentication, so any non-empty string works
- `model_name` — not all models handle tool calling well. `qwen2.5:7b` and `llama3.1:8b` are known to work

On the cluster, replace `localhost:7860` with your cluster's Langflow route URL.
#### Pointing to a remotely hosted model

Update the **KServe vLLM** component in the Langflow UI:

### Stop the local stack
| Field | Value |
|-------|-------|
| api_base | your-model-endpoint/v1 |
| model_name | your-model-id |
| api_key | your-api-key |

### Running the Agent

Run the agent from the Langflow UI by clicking the **Play** button.

### Tracing

Langfuse v3 tracing is included in the local stack and starts automatically. No additional setup needed.

- **Langfuse UI**: http://localhost:3000
- **Login**: admin@langflow.local / password auto-generated in `local/.env`

After running the agent, select the **Langflow Agent** project and click **Traces** to see agent executions — LLM calls, tool invocations, inputs, and outputs.

#### Stopping the stack

```bash
./cleanup-local.sh # stop services, keep data
./cleanup-local.sh --force # stop services, remove all data
make stop # stop services, keep data
make clean # stop services, remove all data
```

**What gets preserved vs wiped:**

| Data | `cleanup-local.sh` | `cleanup-local.sh --force` |
|------|---------------------|----------------------------|
| Downloaded Ollama models (e.g., qwen2.5:7b) | Kept | Kept (stored on host, not in containers) |
| Data | `make stop` | `make clean` |
|------|-------------|--------------|
| Imported Langflow flows | Kept | **Deleted** (re-import needed) |
| Langfuse traces (ClickHouse + MinIO) | Kept | **Deleted** |
| PostgreSQL data | Kept | **Deleted** |
| Redis cache | Kept | **Deleted** |
| `.env` and `.ollama-enabled` config | Kept | **Deleted** |
| `local/.env` config | Kept | **Deleted** |

---

## Deploy to cluster
## Deploying to Cluster

There is nothing to build or deploy — no Dockerfiles, no Helm charts, no k8s manifests. This agent assumes Langflow, Langfuse, and an LLM (LlamaStack/KServe) are already running on the cluster. You just import the flow JSON into the existing Langflow instance and configure it.

### Finding cluster endpoints

Reach out to your cluster admin for the Langflow URL and LlamaStack endpoint/model names. However, if you have `oc` CLI access, you can find them yourself:
Reach out to your cluster admin for the Langflow URL and LlamaStack endpoint/model names. If you have `oc` CLI access, you can find them yourself:

```bash
# Langflow UI URL
Expand Down Expand Up @@ -153,85 +161,29 @@ oc get inferenceservice --all-namespaces
- **NPS Park Alerts**: set `api_key` (same NPS key)
4. Run the agent

## API Endpoints

---

## Pointing agent to different models

### Local model (Ollama)

By default, the local stack runs **qwen2.5:7b** on Ollama. After importing the flow, set these values in the **KServe vLLM** component:

| Field | Value |
|-------|-------|
| api_base | http://host.containers.internal:11434/v1 |
| model_name | qwen2.5:7b |
| api_key | dummy |

> Ollama runs natively on your host machine (not in a container) for GPU acceleration. Use `host.containers.internal` so containerized Langflow can reach it. Ollama doesn't require authentication, so api_key can be any non-empty string (e.g., dummy).

If you want to use a different model:

1. Pull the model on Ollama:
```bash
ollama pull <model-name>
```
Or edit `local/.env` to change the default model and restart:
```
OLLAMA_MODEL=llama3.1:8b
```
Then: `./cleanup-local.sh && ./deploy-local.sh`

2. Update the **KServe vLLM** component in the Langflow UI:

| Field | Value |
|-------|-------|
| api_base | http://host.containers.internal:11434/v1 |
| model_name | your-model-name |

> **Note:** Make sure you have enough CPU/GPU resources locally to run the model. Also, not all models handle tool calling well — for example, smaller models like Llama 3.2 1B may fail at tool calling, causing connection errors when the agent tries to invoke tools. Models like `qwen2.5:7b` and `llama3.1:8b` are known to work well with tool calling.

### Remote model (instead of Ollama)

If you primarily work with a remote model endpoint, or don't have enough local CPU/GPU resources to host a model, you can skip installing Ollama entirely by answering **n** when prompted by `deploy-local.sh`.

Then update the **KServe vLLM** component in the Langflow UI to point to your remote endpoint:

| Field | Value |
|-------|-------|
| api_base | your-model-endpoint/v1 |
| model_name | your-model-id |
| api_key | your-api-key |

---

## Viewing traces in Langfuse

After running the agent, traces are automatically sent to Langfuse.
### POST /api/v1/run/\<flow-id\>

**Locally:**
1. Open http://localhost:3000 (login: admin@langflow.local / password auto-generated in `local/.env`)
2. Select the **Langflow Agent** project
3. Click **Traces** in the left sidebar
4. Click on any trace to see the full agent execution — LLM calls, tool invocations, inputs, and outputs
```bash
curl -X POST http://localhost:7860/api/v1/run/<flow-id> \
-H "Content-Type: application/json" \
-d '{"input_value": "What is the best day to hike near Denver?", "output_type": "chat", "input_type": "chat"}'
```

**On cluster:**
1. Open the Langfuse route on your cluster
2. Select the **Langflow Agent** project
3. Click **Traces** in the left sidebar
Replace `<flow-id>` with the flow ID from the Langflow UI. You can find it in the browser URL bar when you open the flow — e.g., `http://localhost:7860/flow/27e7203d-b2b1-4700-962a-144a66155f14` → the flow ID is `27e7203d-b2b1-4700-962a-144a66155f14`.

---
On the cluster, replace `localhost:7860` with your cluster's Langflow route URL.

## Exporting flows
## Exporting Flows

When exporting a flow from Langflow, API keys and secrets can be embedded in the exported JSON file. To avoid leaking secrets:

1. In the export dialog, **uncheck "Save with API keys"** — this excludes all API keys from the exported file
2. If you already exported with keys included, you can strip them manually by searching for `"api_key"` fields in the JSON and clearing their `"value"` entries

---

## APIs Used
## Resources

- [Langflow Documentation](https://docs.langflow.org/)
- [Open-Meteo](https://open-meteo.com/) — Weather and air quality (free, no key required)
- [National Park Service API](https://developer.nps.gov) — Park search and alerts (free key required)
5 changes: 5 additions & 0 deletions agents/langflow/simple_tool_calling_agent/agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: langflow-simple-tool-calling-agent
displayName: "Langflow Simple Tool Calling Agent"
framework: langflow
description: "Tool-calling agent built with Langflow's visual flow builder. Calls external APIs as tools and reasons over results."
deploymentModel: flow-import

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.

Can also add the env block here similar to other agents

@mpk-droid mpk-droid Apr 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought about it. But langflow's local env vars are auto-generated and not user-defined(eg POSTGRESS DB pwd, uname etc). hence didn't add the env vars.

18 changes: 1 addition & 17 deletions agents/langflow/simple_tool_calling_agent/cleanup-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,6 @@ LOCAL_DIR="$SCRIPT_DIR/local"

cd "$LOCAL_DIR" || { echo "ERROR: Directory $LOCAL_DIR not found."; exit 1; }

stop_ollama() {
if pgrep -f "ollama serve" >/dev/null 2>&1; then
pkill -f "ollama serve" || true
if pgrep -f "ollama serve" >/dev/null 2>&1; then
echo "WARNING: Could not stop Ollama. Stop it manually: pkill -f 'ollama serve'"
else
echo "Ollama stopped."
fi
fi
}

if [ "${1:-}" = "--force" ]; then
echo "Stopping services and removing all data..."
# Stop containerized services
Expand All @@ -32,16 +21,11 @@ if [ "${1:-}" = "--force" ]; then
done
# Remove project network
podman network rm local_default || true
# Stop native Ollama
stop_ollama
# Clean up config
rm -f "$LOCAL_DIR/.ollama-enabled"
rm -f "$LOCAL_DIR/.env"
echo "Done. All services stopped and data removed."
else
echo "Stopping services (data preserved)..."
podman-compose down --remove-orphans || true
# Stop native Ollama
stop_ollama
echo "Done. Run './deploy-local.sh' to start again."
echo "Done. Run 'make run' to start again."
fi
Loading
Loading