Skip to content

Repository files navigation

Jarvis

Python 3.13+ Code style: black OpenRouter Version 0.20.0 License: MIT

A personal AI assistant built from first principles to solve the vendor lock-in problem in conversational AI.

Jarvis Header Image

Motivation

Most professionals rely on ChatGPT, Claude, Gemini, or Copilot subscriptions to interact with AI. These tools are powerful, but they create a critical dependency: all your context, conversation history, and learned preferences are locked within each provider's ecosystem.

As someone learning AI Engineering, I wanted to solve this problem for myself while documenting the journey. Jarvis is the result: a provider-agnostic personal assistant that:

  • Maintains persistent context and conversation history that I control
  • Works with any LLM provider through a unified interface (currently OpenRouter)
  • Stores everything locally in human-readable markdown files
  • Can be extended and customized as my needs evolve

This project demonstrates my approach to learning: build solutions to real problems, keep them simple, and document the reasoning behind every decision.

How It Works

Jarvis follows a straightforward architecture that prioritizes clarity and maintainability:

┌─────────────────┐
│   Context Files │  (personal_context.md, preferences.md, current_focus.md)
│   (Markdown)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Context Builder │  Assembles system prompt from context files
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│     Agent       │  Data-driven (meta.yaml) or Python class
│  (Orchestrator  │  Specialist agents for focused tasks
│  or Specialist) │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│     Tools       │  Web fetch, conversation recall, etc.
│  (Agentic Loop) │  Max 5 iterations per request
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Stream Handler  │  Streams responses from any provider (via OpenRouter)
└────────┬────────┘
         │
         ▼
┌─────────────────┐     ┌─────────────────┐
│ Conversation    │────▶│   RAG Index     │  Semantic search over history
│ Memory          │     │  (ChromaDB,     │  (optional, opt-in)
│                 │     │   optional)     │
└─────────────────┘     └─────────────────┘

Key Design Principles

  1. Human-readable storage: All context and conversations are stored as markdown or JSON files you can edit directly
  2. Provider independence: Switching from Claude to GPT-4 is a one-line config change
  3. Simplicity first: No unnecessary abstractions—just clean functions that do one thing well
  4. Local-first: Your data lives on your machine, not in someone else's cloud

Features

  • Agent Framework: Slash-command routing to specialist agents (Writer, Researcher, Simplifier, Navigator, Tactics Coach, Content Reviewer, Substack Publisher, Substack Image Creator, OKR Architect, Obsidian Note Creator, Pattern Language Expert, Pattern Card Generator, Strategyzer, Developer, Reading Assistant)
  • Data-Driven Agents: Most agents defined via meta.yaml + prompts/system.md -- no Python class needed
  • Standalone Agent Mode: Run any agent directly with --agent <name>
  • Tool Calling: Agentic loop with tool execution (max 5 iterations per request)
  • Web Fetch Tool: URL fetching with content extraction (httpx + trafilatura)
  • Conversation Recall (RAG): Semantic search over conversation history via ChromaDB (opt-in)
  • Vault Semantic Search: Meaning-based search over the Obsidian vault via the Cortex MCP server (opt-in, HUB-01)
  • Enhanced CLI UX: Rich terminal formatting, markdown rendering, prompt_toolkit with paste support and input history
  • Persistent Personal Context: Define who you are, your preferences, and current focus areas in simple markdown files
  • Conversation Memory: All interactions are logged with timestamps, creating a searchable history
  • Streaming Responses: Real-time token-by-token output for a responsive chat experience
  • Non-Streaming Mode (opt-in): Toggle with /stream or configure via models.streaming. Enables prompt caching via OpenRouter (blocked in streaming mode due to upstream LiteLLM format inconsistency)
  • History Summarization (opt-in): Compresses old conversation turns when history exceeds ~40K tokens, using Gemini Flash to reduce costs in long sessions. Enable via summarization.enabled in config.
  • Provider Agnostic: Unified interface to multiple LLM providers through OpenRouter/LiteLLM
  • Token & Cost Tracking: Automatic tracking of usage and costs per request and session
  • Latency Metrics: TTFT and total latency captured per response
  • Simple Configuration: YAML-based config with sensible defaults
  • Obsidian Integration: Generate daily note summaries from conversation history
  • Things 3 Integration: Auto-sync tasks from Things 3 (macOS) via SQLite for task-aware responses. Write tools (create_task, complete_task, update_task) available via things3_tools group.
  • MCP Client Integration: Connect to external MCP (Model Context Protocol) servers. MCP server tools are bridged into the ToolDefinition system and appear as regular tool groups. Supports stdio, SSE, and streamable HTTP transports. Config-only setup via mcp.servers in config/local.yaml.
  • Comprehensive Testing: Automated test suite with high code coverage + mutation testing via mutmut
  • Benchmark Cost Estimation: Estimate golden test run costs per model before evaluation
  • Conversation Import: Import ChatGPT and Claude exports into Jarvis format

Getting Started

Prerequisites

Installation

# Clone the repository
git clone https://github.com/Cherubeam/jarvis.git
cd jarvis

# Install dependencies using uv (https://github.com/astral-sh/uv)
uv sync

# Set up your environment variables
echo "OPENROUTER_API_KEY=your_key_here" > .env

# Configure your personal context
# Edit the files in data/context/:
# - personal_context.md (who you are)
# - professional_context.md (professional background)
# - preferences.md (how the assistant should behave)
# - current_focus.md (what you're working on)

Usage

# Start JARVIS (default orchestrator)
uv run jarvis

# Run a specialist agent directly
uv run jarvis --agent writer
uv run jarvis --agent researcher
uv run jarvis --agent simplifier
uv run jarvis --agent navigator
uv run jarvis --agent tactics_coach
uv run jarvis --agent developer

GUI (Phases 1–8)

A graphical peer to the CLI, sharing the same agents, tools, conversation files, and approval flow:

uv sync --extra web
uv run jarvis-gui              # prints a "Sign in:" URL and opens it
uv run jarvis-gui --no-browser # just serve — open the printed URL yourself

The GUI requires authentication. Opening the printed Sign in: URL once signs that browser in; the token lives in data/.gui_token. Scripts can use Authorization: Bearer $(cat data/.gui_token) instead. See docs/engineering/gui.md#authentication.

Shipped surfaces: Chat (streaming, tool cards, vault-write approval diffs, command palette, Tweaks panel, light/dark + accent swap, click a sidebar row to resume that conversation in-place — your next message appends to the same JSON file), Home (greeting, Things 3 tasks, cost-this-week, resume, recent, quick-start), History (two-pane filterable conversation browser with per-conversation hard-delete and one-click resume from the detail pane), Sidebar Timeline mode (togglable day-axis variant), Agents (categorized grid + per-agent detail with tools, recent sessions, 14-day cost sparkline, and "start session →" launcher), the Agent Prompt Editor (Prompt / Versions / Stats / Context tabs on each agent — edit system.md, snapshot-on-save history with restore, resolved-prompt preview), the Includes editor (Includes tab — edit shared and local prompt fragments like voice-profile.md in place, with shared-write modal confirm + per-include snapshot history), Outcomes (score pending recommendations — happened / didn't / partial + quality 1-5 + note), and Settings (every field in the typed Settings model editable across 16 sections with inline descriptions, customized-dot overrides, model-validator error display, field-level hot-apply gating that reports which changes took effect live vs. need a restart, and a managed-header guard that prevents first-save from overwriting a hand-edited config/local.yaml). See docs/engineering/gui.md for architecture + rebuild instructions.

During a chat session, you can use slash commands:

/write <text>           Delegates to Writer agent (prose, editing, rewriting)
/research <text>        Delegates to Researcher agent (analysis, synthesis)
/simplify <text>        Delegates to Simplifier agent (explains complex ideas simply)
/review                 Enters Content Reviewer session (structured evaluation)
/publish                Enters Substack Publisher session (pre-pub workflow)
/substack-image         Enters Substack Image Creator session (header image prompts)
/navigator              Enters Navigator agent session (alignment, weekly reviews)
/tactics                Enters Tactics Coach agent session (Pip Decks coaching)
/okr-architect          Enters OKR Architect agent session
/obsidian-note-creator  Enters Obsidian Note Creator session (evergreen note extraction)
/pattern-language-expert  Enters Pattern Language Expert session
/pattern-cards          Enters Pattern Card Generator session (visual cards from patterns)
/strategize             Enters Strategyzer session (competitive analysis, growth, pricing)
/develop                Enters Developer agent session (codebase, git, tests)
/daily-summary [date]   Generates an Obsidian daily note summary (default: today)
/outcomes               Reviews pending tracked recommendations (score + retrospective)
/stream                 Toggles between streaming and non-streaming response modes

Type quit or exit to end the session.

Troubleshooting

ModuleNotFoundError: No module named 'apps' when running uv run jarvis

On macOS with Python 3.13+, the editable-install .pth file can get a hidden flag (UF_HIDDEN) that causes Python to skip it during startup. Fix it with:

# Remove the hidden flag from the .pth file
chflags nohidden .venv/lib/python3.13/site-packages/_jarvis.pth

# Or recreate the virtual environment from scratch
rm -rf .venv && uv sync

Importing Conversations

# ChatGPT
uv run python scripts/import_chatgpt.py imports/conversations.json --dry-run
uv run python scripts/import_chatgpt.py imports/conversations.json
uv run python scripts/import_chatgpt.py imports/conversations.json --date-from 2025-01-01 --model gpt-4o --include-archived

# Claude conversations
uv run python scripts/import_claude.py imports/conversations.json --dry-run
uv run python scripts/import_claude.py imports/conversations.json
uv run python scripts/import_claude.py imports/conversations.json --date-from 2025-01-01

# Claude context (memories + projects)
uv run python scripts/import_claude_context.py --dry-run
uv run python scripts/import_claude_context.py
uv run python scripts/import_claude_context.py --memories imports/memories.json --projects imports/projects.json

Imports are idempotent — re-running safely updates existing conversations with new messages and title changes (Claude), or skips unchanged conversations (ChatGPT).

Connecting MCP Servers

JARVIS can connect to external MCP servers and use their tools alongside native ones. Adding or removing a server is a config-only change — no code edits required.

Step 1: Enable MCP and declare servers in config/local.yaml:

mcp:
  enabled: true
  servers:
    # Example: filesystem access via stdio
    filesystem:
      transport: stdio
      command: npx
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/Documents"]
      tool_group: fs_tools           # name used in agent meta.yaml
      timeout_seconds: 30            # per-call timeout (default: 30)

    # Example: remote server via SSE
    github:
      transport: sse
      url: "http://localhost:3001/sse"
      headers:
        Authorization: "Bearer your-token-here"
      tool_group: github_tools

    # Example: remote server via streamable HTTP
    my_api:
      transport: streamable_http
      url: "http://localhost:8080/mcp"
      tool_group: my_api_tools

Each server key (e.g. filesystem) is used for tool namespacing — MCP tool read_file from server filesystem becomes mcp_filesystem__read_file in JARVIS. The tool_group field (defaults to the server key if omitted) is the name you reference from agents.

Step 2: Assign tool groups to agents. Add the tool_group name to the agent's meta.yaml:

# packages/agents/researcher/meta.yaml
tools:
  - web_tools
  - fs_tools        # MCP server tool group
  - github_tools    # another MCP server tool group

Step 3: Restart JARVIS. You should see the tools load at startup:

[MCP] 5 tool(s) from 2 server(s).

Giving MCP tools to the JARVIS orchestrator: By default, only delegate agents receive MCP tools (via meta.yaml). To make MCP tools available to the JARVIS orchestrator itself, add the tool group to jarvis_tools in apps/cli/main.py:

jarvis_tools = (
    list(shared_tools)
    + tool_groups.get("web_tools", [])
    + tool_groups.get("things3_tools", [])
    + tool_groups.get("fs_tools", [])       # add your MCP tool group here
)

Transport reference:

Transport Required fields Use case
stdio command, args (optional) Local servers launched as child processes
sse url Remote servers with Server-Sent Events
streamable_http url Remote servers with HTTP streaming

Optional fields for all transports: tool_group, timeout_seconds, headers (SSE/HTTP only), env, cwd (stdio only).

Troubleshooting:

  • If a server fails to connect at startup, JARVIS logs a warning and continues — other servers and native tools are unaffected.
  • If a tool call fails at runtime, the error is returned to the LLM as tool output so it can adapt.
  • stdio servers require the command to be available on your PATH (e.g. npx requires Node.js).
  • To verify which tools loaded, check the [MCP] line in startup output.

Switching LLM Providers

Edit config/default.yaml or config/local.yaml:

models:
  default: "openrouter/anthropic/claude-sonnet-4.6"  # Change to desired model

See docs/engineering/deployment.md for full provider configuration.

Project Structure

jarvis/
├── apps/                               # Deployable applications
│   ├── cli/                            # CLI entry point
│   │   ├── main.py                     # Command-line interface + slash-command loop
│   │   ├── display.py                  # Rich terminal formatting
│   │   ├── session_factory.py          # build_session() — shared CLI/GUI bootstrap
│   │   └── review.py                   # /outcomes scoring helpers (public API for GUI)
│   └── gui/                            # JARVIS GUI (Phases 1–8)
│       ├── main.py                     # Entry: uvicorn.run + webbrowser.open
│       ├── server/                     # FastAPI backend
│       │   ├── app.py                  # Factory + lifespan (MCP start/stop, logger save)
│       │   ├── state.py                # GuiSession + per-turn handlers
│       │   ├── protocol.py             # WebSocket TypedDicts (server ↔ client)
│       │   ├── streaming.py            # WebStreamHandler — on_event → queue events
│       │   ├── confirmation.py         # WebConfirmationHandler — diff buffer + threaded wait
│       │   ├── bridge.py               # Per-turn orchestration (agent.run in to_thread)
│       │   ├── agents/                 # Agent detail + prompt-history helpers
│       │   ├── home/                   # cost_week + task_links rollups
│       │   ├── history/                # Conversations index + derive helpers
│       │   └── routes/                 # api · chat_ws · agents · agent_includes ·
│       │                               #   conversations · home · outcomes · settings
│       └── web/                        # React 18 + Vite + TypeScript frontend
│           ├── src/                    # React + TypeScript source
│           └── dist/                   # Built bundle (committed; rebuild with `npm run build`)
│
├── packages/                           # Shared libraries
│   ├── core/                           # Core functionality
│   │   ├── llm_client.py               # Unified LLM provider interface
│   │   ├── context_builder.py          # Assembles system prompts from context
│   │   ├── stream_handler.py           # Streaming response handler with agentic loop
│   │   ├── memory.py                   # Conversation logging (schema v1.0.0)
│   │   ├── pricing.py                  # Cost calculation and tracking
│   │   ├── settings.py                 # Typed pydantic-settings model + load_config() + classify_changes
│   │   ├── frontmatter.py              # YAML frontmatter parse/dump + atomic write
│   │   ├── date_utils.py               # parse_relative_date (ISO + "next week", "1 month", etc.)
│   │   ├── daily_summary.py            # /daily-summary request builder (CLI + GUI shared)
│   │   ├── events.py                   # Typed event dataclasses for streaming decoupling
│   │   ├── filesystem_access.py        # Filesystem access control (FilesystemGuard)
│   │   ├── card_renderer.py             # Pattern card rendering (HTML/PNG via WeasyPrint)
│   │   ├── benchmark_costs.py          # Benchmark cost estimation
│   │   ├── importers/                  # Conversation importers (ChatGPT, Claude)
│   │   ├── rag/                        # Conversation recall (optional, ChromaDB)
│   │   │   ├── indexer.py              # Startup scan, message-pair chunking
│   │   │   └── searcher.py             # Cosine similarity search with date filters
│   │   └── tools/                      # Tool calling infrastructure
│   │       ├── base.py                 # ToolDefinition + ToolRegistry
│   │       ├── executor.py             # Tool call execution
│   │       ├── web_fetch.py            # URL fetch (httpx + trafilatura)
│   │       ├── conversation_recall.py  # RAG search tool
│   │       ├── delegate.py             # Agent delegation tool
│   │       ├── vault_read_tools.py     # Obsidian vault read tools
│   │       ├── vault_write_tools.py    # Obsidian vault write tools (scoped per agent)
│   │       ├── web_search.py            # DuckDuckGo web search tool
│   │       ├── blog_tools.py           # Blog management tools
│   │       ├── card_generator_tools.py # Pattern card generator tools
│   │       ├── things3_tools.py        # Things 3 task management tools
│   │       ├── codebase_tools.py       # Codebase analysis tools
│   │       ├── git_tools.py            # Git operations tools
│   │       ├── project_write_tools.py  # Project file write tools
│   │       ├── test_tools.py           # Test runner tool
│   │       ├── mutation_tools.py       # Mutation testing tools (mutmut)
│   │       ├── suggest_improvements.py # Content improvement suggestions
│   │       └── content_evaluator.py    # LLM-as-judge evaluation tool
│   ├── agents/                         # Agent implementations
│   │   ├── base.py                     # Base agent class + DataDrivenAgent
│   │   ├── registry.py                 # Filesystem-based agent auto-discovery
│   │   ├── jarvis/                     # Main JARVIS orchestrator agent (Python class)
│   │   ├── _shared/                    # Shared prompt includes (voice-profile, anti-patterns)
│   │   ├── writer/                     # Writer — drafting & editing
│   │   ├── content_reviewer/           # Content Reviewer — structured evaluation
│   │   ├── substack_publisher/         # Substack Publisher — pre-pub workflow
│   │   ├── substack_image_creator/     # Substack Image Creator — header image prompts
│   │   ├── researcher/                 # Researcher — analysis, synthesis
│   │   ├── simplifier/                 # Simplifier — explains complex ideas simply
│   │   ├── tactics_coach/              # Tactics Coach — Pip Decks coaching
│   │   ├── navigator/                  # Navigator — alignment, weekly reviews
│   │   ├── okr_architect/              # OKR Architect
│   │   ├── obsidian_note_creator/      # Obsidian Note Creator
│   │   ├── pattern_language_expert/    # Pattern Language Expert
│   │   ├── pattern_card_generator/    # Pattern Card Generator (visual cards)
│   │   ├── strategyzer/              # Strategyzer (competitive analysis, growth)
│   │   └── developer/                 # Developer agent (git sandbox, code tools)
│   ├── skills/                         # Skills (passive knowledge packs for card indexing)
│   │   ├── base.py                     # BaseSkill (parses SKILL.md, optional skill.py)
│   │   ├── registry.py                 # Filesystem-based skill discovery
│   │   ├── resolver.py                 # Skill resolution and binding for agents
│   │   ├── content-evaluator/          # Content evaluation (SKILL.md + skill.py)
│   │   └── .../                        # Additional skills (each has SKILL.md)
│   ├── integrations/                   # External service integrations
│   │   ├── things3/                    # Things 3 task sync + write tools
│   │   ├── mcp/                        # MCP client integration
│   │   │   ├── config.py               # Config parsing + validation
│   │   │   ├── client.py               # Connection lifecycle + async/sync bridge
│   │   │   └── bridge.py               # MCP Tool → ToolDefinition conversion
│   │   └── obsidian/                   # Obsidian vault integration
│   │       ├── vault.py                # Vault reader with symlink protection
│   │       ├── callout.py              # Callout block parser
│   │       ├── diff.py                 # Diff computation and formatting
│   │       └── writer.py               # Note writer with confirmation
│   └── telemetry/                      # Metrics and evaluation
│
├── data/                               # User data
│   ├── context/                        # Your personal context files
│   │   ├── personal_context.md         # Who you are
│   │   ├── professional_context.md     # Professional background
│   │   ├── preferences.md              # Assistant behavior preferences
│   │   ├── current_focus.md            # Current projects and priorities
│   │   ├── tasks.md                    # Auto-synced from Things 3
│   │   └── projects/                   # Project-specific context
│   ├── conversations/                  # Timestamped conversation logs (by year)
│   │   ├── 2024/                      # e.g. 2024-02-10_17-50-05.json
│   │   ├── 2025/
│   │   └── 2026/
│   └── rag/                            # ChromaDB vector store (runtime, gitignored)
│
├── scripts/                            # Utility scripts
│   ├── import_chatgpt.py               # ChatGPT conversation importer
│   ├── import_claude.py                # Claude conversation importer
│   ├── import_claude_context.py        # Claude context importer
│   ├── model_benchmark.py              # Model benchmark runner
│   ├── benchmark_report.py             # Benchmark report generator
│   ├── analyze_costs.py                # Cost analysis
│   ├── analyze_context.py              # Context utilization analyzer
│   └── link_skills.sh                  # Symlink private skills repo
│
├── config/                             # Configuration
│   ├── default.yaml                    # Default configuration
│   └── local.yaml                      # Local overrides (gitignored)
│
├── tests/                              # Comprehensive test suite
│   ├── unit/                           # Unit tests
│   ├── integration/                    # Integration tests
│   ├── golden/                         # Golden test conversations + LLM-as-judge
│   └── README.md                       # Testing guide
│
├── docs/                               # Documentation
│   ├── product/                        # Product specs and roadmap
│   ├── engineering/                    # Technical documentation
│   └── research/                       # AI engineering research
│
└── pyproject.toml                      # Project configuration

Roadmap

This is a learning project, and I'm building it iteratively. Workstreams use the initiative/milestone naming scheme (ADR-033); current priorities:

FND — Foundation & Metrics (Complete ✅)

  • Basic chat interface with persistent context
  • Conversation logging and history
  • Token usage tracking and cost calculation
  • LiteLLM integration for provider flexibility
  • Comprehensive testing framework

EVAL — Evaluation & Quality Metrics (Complete ✅)

  • 10 golden test conversations defined
  • LLM-as-judge automated evaluation (~$0.41/run)
  • Things 3 integration (SQLite-based task sync via things.py)
  • Latency tracking (TTFT)
  • Model comparison benchmarks
  • Benchmark cost estimation per model
  • Conversation schema v1.0.0 (structured logging with migration support)
  • ChatGPT conversation import (bulk import with filters)
  • Claude conversation import (bulk import with date filters)

CTX — Context & Integrations (Complete ✅)

  • Context builder with frontmatter selective loading
  • Obsidian daily note integration (/daily-summary)

AGENT — Agent Framework (Complete ✅)

  • Base agent class with prompt loading
  • Agent registry with filesystem-based auto-discovery
  • Specialist agents: Writing, Research, Clarity
  • Slash-command routing and standalone --agent mode
  • StreamHandler extraction from CLI

CAP — Agent Capabilities (In Progress)

  • Tool calling infrastructure (ToolDefinition, ToolRegistry, agentic loop)
  • Web fetch tool (httpx + trafilatura)
  • Conversation recall via RAG (ChromaDB, opt-in)
  • Enhanced CLI UX (rich rendering, prompt_toolkit)
  • Skills framework (SKILL.md-driven, vendor-portable, used as passive knowledge packs)
  • JARVIS delegation (orchestrator auto-routes to specialists)
  • Extended tools — web search (DuckDuckGo + URL fetch via web_tools group)
  • Outcome tracking (track_recommendation, /outcomes, recall_outcomes — closed loop on advice)
  • Readwise / Reading Assistant integration (/reading)
  • Extended tools — Playwright browser automation
  • Intelligent model routing (task complexity → model selection)

WEB — Web Interface (Complete ✅ — WEB-01WEB-08 shipped)

  • Event decoupling prerequisite (typed events, StreamHandler emission) + typed configuration (packages/core/settings.py + in-GUI Settings editor)
  • Chat shell + Conversations browser + Home + Sidebar Timeline (0.17.0)
  • Agents overview + Agent Detail + Prompt Editor + Outcomes view (0.19.0)
  • Settings editor + Prompt-include editor + hot-apply gating (0.20.0)
  • Interactive delegation sub-loops (deferred)
  • Move print statements from StreamHandler into CLI adapter

AON — Always-On & Loop Engineering (Planned)

  • Safety rails + shared TurnRunner, scheduled briefing, Telegram, headless Mac host, eval hardening (AON-01AON-05)

Later initiatives:

  • TOK — context-window management: history summarization (opt-in, ~40K threshold) and tool result trimming
  • OPS — system monitoring and optimization

See docs/product/roadmap.md for detailed plans.

Benchmarking

Estimate benchmark costs anytime (uses latest golden run baseline):

uv run python scripts/model_benchmark.py

To run evaluations after the estimate (paid), add --evaluate.

Generate the benchmark comparison table in docs:

uv run python scripts/benchmark_report.py

What I'm Learning

Building Jarvis is teaching me:

  • System design for AI applications: How to structure context, manage conversation state, and handle streaming responses
  • API integration patterns: Working with multiple LLM providers through a unified interface
  • Prompt engineering: Crafting effective system prompts that incorporate personal context
  • Data persistence strategies: Balancing human-readability with queryability
  • Token economics: Understanding context windows, truncation, and cost optimization

Why This Matters

This project demonstrates several things I value as an engineer:

  1. Problem-first thinking: I identified a real pain point (vendor lock-in) and built a solution
  2. Learning by building: Theory is great, but shipping code is how I learn best
  3. Simplicity over cleverness: The codebase is intentionally straightforward—no premature optimization or over-engineering
  4. Documentation: Every design decision is explained (see code comments and this README)
  5. Iterative development: Start simple, ship early, improve based on real usage

Tech Stack

  • Language: Python 3.13
  • LLM Provider: LiteLLM + OpenRouter (unified API for Claude, GPT-4, Gemini, etc.)
  • Terminal UI: rich + prompt_toolkit
  • GUI Backend: FastAPI + WebSockets (uvicorn)
  • GUI Frontend: React 18 + Vite + TypeScript (built bundle committed)
  • Storage: Local filesystem (markdown + JSON)
  • Vector DB: ChromaDB (optional, for conversation recall / RAG)
  • HTTP: httpx + trafilatura (web fetch tool)
  • Configuration: YAML + pydantic-settings (typed) + environment variables
  • Code Quality: ruff (lint + format) + mypy (strict=true); CI + pre-commit hooks
  • Testing: pytest + mutmut (details)
  • Package Management: uv (fast Python package installer)

Contributing

This is primarily a personal learning project, but if you find it useful or have suggestions, feel free to open an issue!

License

MIT License - see LICENSE for details.


Built by Marco Braun | Learning AI Engineering one commit at a time

About

A provider-agnostic personal AI assistant that solves vendor lock-in by maintaining persistent context and conversation history locally. Learning project documenting my AI Engineering journey.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages