Skip to content

Repository files navigation

llm4s-tripper

A travel planning agent built with llm4s and workflows4s, demonstrating functional programming patterns for agentic AI applications.

Overview

llm4s-tripper is a Scala 3 application that creates personalized travel itineraries using:

  • llm4s: Type-safe LLM interactions with structured outputs
  • workflows4s: Deterministic workflow orchestration with WIO composition
  • Cats Effect: Functional effect system for IO and concurrency
  • MCP (Model Context Protocol): Real-time data from Google Maps, Brave Search, and more

Current Status

Component Status
Workflow Engine ✅ Complete - workflows4s WIO composition
LLM Integration ✅ Complete - OpenAI supported
MCP Integration ✅ Complete - Real POI discovery
CLI Interface ✅ Complete
Tests ✅ 329 tests passing

See docs/IMPLEMENTATION_STATUS_REPORT.md for detailed status.

Architecture

The application follows a feature-based package organization:

io.github.llm4s.tripper/
├── cli/         # Command-line interface
├── config/      # Configuration (PureConfig)
├── domain/      # Domain model (TravelBrief, POI, TravelPlan)
├── llm/         # LLM client factory and configuration
├── logging/     # Structured logging with log4cats
├── mcp/         # MCP client manager, tool registry, and rate limiting
└── workflow/    # Workflow definitions and steps
    ├── runtime/ # WorkflowRuntimeFactory, InstanceManager
    └── steps/   # POI Discovery, Research, Plan Generation, etc.

Workflow Pipeline

The travel planning workflow uses workflows4s WIO composition:

POI Discovery >>> POI Research >>> Plan Generation >>> Accommodation Discovery >>> Plan Finalization

Each step is a WIO[State, Error, State] composed using the >>> operator.

Key Patterns

  • Functional Core, Imperative Shell: Pure domain logic with effects at the edges
  • Type-Safe LLM Interactions: Circe-based JSON encoding/decoding for structured outputs
  • WIO Workflow Composition: workflows4s >>> operator for step sequencing
  • Parallel Execution: Cats Effect parTraverse with Semaphore for concurrency control
  • StepProvider Abstraction: Switch between real MCP and mock implementations
  • Configuration as Code: PureConfig with environment variable overrides

Prerequisites

  • JDK 21+
  • sbt 1.11.7+
  • Scala 3.7.3
  • Docker Desktop (for real MCP integration)
  • $HOME/.docker-java.properties with api.version=1.44

Required API Keys

# LLM Provider (required)
export OPENAI_API_KEY=your_openai_api_key

# For real MCP integration (optional)
export GOOGLE_MAPS_API_KEY=your_google_maps_api_key
export BRAVE_API_KEY=your_brave_search_api_key

Getting Started

# Compile the project
sbt compile

# Run tests (329 tests)
sbt test

# Run with real MCP tools (default - requires API keys)
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture -o paris-trip.json"

# Run with mock data (for development/testing without API keys)
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture --useMockData -o paris-trip.json"

CLI Usage

The application provides a command-line interface for planning trips.

Basic Usage

# Real MCP mode (default) - uses Google Maps, Brave Search, etc.
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture -o my-paris-trip.json"

# Mock mode - for development without external API calls
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture --useMockData -o my-paris-trip.json"

Plan from JSON File

# Create trip-request.json:
{
  "from": "New York, USA",
  "to": "Paris, France",
  "startDate": "2025-06-01",
  "endDate": "2025-06-05",
  "travelers": [
    {
      "name": "John Doe",
      "interests": ["art", "history"],
      "dietaryRestrictions": [],
      "mobilityConsiderations": null
    }
  ]
}

# Run:
sbt "runMain io.github.llm4s.tripper.Main planFromJson -i trip-request.json -o paris-plan.json"

Verbose Output

sbt "runMain io.github.llm4s.tripper.Main plan -d Rome,Italy -s 2025-07-01 -e 2025-07-07 \
  -p history,food -n JaneSmith -i ancient-ruins,cuisine -v -o rome-trip.json"

Available Commands

Command Description
plan Plan a trip using command-line arguments
planFromJson Plan a trip using a JSON input file
version Display version information
help Show usage help

Command Options

Option Short Description
--destination -d Destination (required)
--startDate -s Start date YYYY-MM-DD (required)
--endDate -e End date YYYY-MM-DD (required)
--preferences -p Comma-separated preferences
--name -n Traveler name
--interests -i Comma-separated interests
--output -o Output file (default: travel-plan.json)
--verbose -v Enable verbose logging
--useMockData Use mock data instead of real MCP (for dev/testing)

Execution Modes

llm4s-tripper supports two execution modes controlled by the --useMockData flag:

1. Real MCP Mode (Default)

Uses real MCP tools for POI discovery, research, and accommodation search. Requires API keys.

# Default - uses real MCP tools
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture -o paris-trip.json"

What happens in real mode:

  • POI Discovery: Calls Google Maps MCP server for real places
  • POI Research: Uses Brave Search and Wikipedia for detailed info
  • Plan Generation: LLM creates itinerary from real data
  • Accommodation: Searches real listings (with fallback to mock)

2. Mock Mode

For development and testing without external API calls. Still requires OPENAI_API_KEY for LLM calls.

# Use --useMockData flag
sbt "runMain io.github.llm4s.tripper.Main plan -d Paris,France -s 2025-06-01 -e 2025-06-05 \
  -p art,history -n JohnDoe -i museums,architecture --useMockData -o paris-trip.json"

What happens in mock mode:

  • POI Discovery: LLM generates POIs without MCP
  • POI Research: LLM researches without external tools
  • Plan Generation: LLM creates itinerary
  • Accommodation: Returns hardcoded mock data

MCP Architecture

The application uses native MCP clients with stdio transport:

┌─────────────────┐     stdio      ┌─────────────────┐
│  llm4s-tripper  │ ◄────────────► │  MCP Server     │
│  (MCPClient)    │   JSON-RPC     │  (Docker)       │
└─────────────────┘                └─────────────────┘

Available MCP Services

Service Purpose API Key Required
google-maps POI discovery, geocoding GOOGLE_MAPS_API_KEY
brave-search Web search for research BRAVE_API_KEY
wikipedia Detailed information None
openbnb-airbnb Accommodation listings None

Setup MCP Servers

# Docker images are pulled automatically by llm4s MCP client
# The following servers are configured:
# - google-maps (requires GOOGLE_MAPS_API_KEY)
# - brave-search (requires BRAVE_API_KEY)
# - wikipedia (no API key required)
# - openbnb-airbnb (no API key required)

# Set API keys
export GOOGLE_MAPS_API_KEY=your_key
export BRAVE_API_KEY=your_key
export OPENAI_API_KEY=your_key

Troubleshooting

Zero POIs discovered:

  • Verify GOOGLE_MAPS_API_KEY has Places API (New) enabled
  • Check Docker Desktop is running
  • Use -v flag for verbose logging

Connection errors:

  • Ensure Docker images are pulled
  • Check $HOME/.docker-java.properties has api.version=1.44

Configuration

Configuration is managed through application.conf in src/main/resources/.

LLM Configuration

tripper {
  llm {
    planner {
      provider = "openai"
      provider = ${?LLM_PLANNER_PROVIDER}
      model = "gpt-4.1"
      model = ${?LLM_PLANNER_MODEL}
      temperature = 0.7
      max-tokens = 4000
      timeout = 60s
    }
    researcher {
      provider = "openai"
      model = "gpt-4.1-nano"
      temperature = 0.5
      max-tokens = 2000
      timeout = 30s
    }
  }
}

Environment Variable Overrides

Variable Description
OPENAI_API_KEY OpenAI API key (required)
LLM_PLANNER_PROVIDER LLM provider for planner role
LLM_PLANNER_MODEL Model for planner role
LLM_PLANNER_TIMEOUT Timeout for planner calls
LLM_RESEARCHER_PROVIDER LLM provider for researcher role
LLM_RESEARCHER_MODEL Model for researcher role
GOOGLE_MAPS_API_KEY Google Maps API key
BRAVE_API_KEY Brave Search API key
MCP_TOOLS_BRAVE_MIN_INTERVAL Rate limit interval for Brave search (default: 1s)
MCP_TOOLS_WIKIPEDIA_MIN_INTERVAL Rate limit interval for Wikipedia (default: 200ms)

Dependencies

Library Version Purpose
workflows4s local Workflow orchestration (WIO)
llm4s local LLM integration
Cats Effect 3.6.3 Effect system
Cats Retry 3.1.0 Retry policies
http4s 0.23.31 HTTP client
Circe 0.14.10 JSON handling
PureConfig 0.17.8 Configuration
log4cats 2.7.0 Logging
mainargs 0.7.6 CLI parsing

Documentation

Document Description
IMPLEMENTATION_STATUS_REPORT.md Current implementation status
IMPLEMENTATION_PLAN.md Original implementation plan
WISHLIST.md Future features (Anthropic, etc.)
REAL_TRIP_PLANNING_IMPLEMENTATION.md Detailed task documentation

License

MIT License

Copyright (c) 2025 Giovanni Ruggiero

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages