Skip to content

im-ng/eva

Repository files navigation

Eva mascot

Eva

License Zig Tests Coverage-86.2-yellow

A high-performance, single-binary AI agent runtime written in Zig.

Connect any LLM to any chat channel. Workspace-scoped tools, hybrid flat-file / SQLite memory, cron-scheduled tasks, and a circuit-breaker that survives LLM outages.

Eva is built on the Zero framework and inspired by the multi-channel agent architecture in nanobot.

It is independent of any LLM vendor — any OpenAI-compatible API works (OpenAI, OpenRouter, Groq, Together, llama.cpp, vLLM, Ollama, etc.).

✨ Features

  • Multi-channel — WebSocket and Telegram out of the box. Discord and more behind a stable vtable.
  • Workspace-scoped toolsread_file, write_file, list_dir, shell, web_fetch. All operations stay inside a configured directory. SSRF protection on web_fetch blocks loopback / RFC1918.
  • Hybrid memory — SQLite for queries, human-editable MEMORY.md / SOUL.md / USER.md for direct editing. A "dream" thread periodically extracts insights; a consolidator summarizes long conversations.
  • Cron scheduler — 6-field cron (sec min hour day month dow) with one-shot reminders. LLM can create_reminder, list_reminders, disable_reminder via natural language.
  • Reliability — Circuit breaker, token-bucket rate limiter, tool-call loop detection, structured key=value logging.
  • Telegram-native HTTPS — Custom BearSSL-based TLS client for long-polling. No external curl / wget needed.
  • Single binary — Static or gcompat-linked. ~7 MB stripped. No runtime dependencies beyond librdkafka.so.1 and ca-certificates.
  • Configuration by env vars — No config files. All knobs are documented in GETTING_STARTED.md.

🏗 Architecture

flowchart TD
    %% External clients
    WS_Client["WebUI Client"]
    TG_Client["Telegram Client"]

    %% Channels
    WS_Channel["WebSocket Channel"]
    TG_Channel["Telegram Channel"]

    %% Message bus
    Inbound["Inbound Queue"]
    Outbound["Outbound Queue"]

    %% Agent loop
    Agent["Agent Loop"]
    ContextBuilder["Context Builder"]
    LLM["LLM Provider"]
    Tools["Tool Registry"]

    %% Session manager
    SessionMgr["Session Manager"]

    %% Reliability
    CB["Circuit Breaker"]
    RL["Rate Limiter"]

    %% Memory
    MemoryStore["Memory Store<br/>(SQLite + flat files)"]
    Consolidator["Consolidator<br/>(auto-summarize)"]
    Dream["Dream<br/>(background insights)"]
    MemoryFiles["MEMORY.md / SOUL.md / USER.md"]

    %% Scheduler
    Cronz["Cronz<br/>(*/30s tick)"]
    Scheduler["Scheduler"]

    %% SQLite
    SQLite[("SQLite<br/>sessions / messages /<br/>memories / cron_jobs")]

    %% Flow: clients → channels
    WS_Client --> WS_Channel
    TG_Client --> TG_Channel

    %% Flow: channels → inbound
    WS_Channel --> Inbound
    TG_Channel --> Inbound

    %% Flow: inbound → agent
    Inbound --> Agent

    %% Flow: agent internals
    Agent --> ContextBuilder
    ContextBuilder --> LLM
    LLM -->|"tool calls"| Tools
    Tools -->|"results"| Agent
    LLM -->|"response"| Agent

    %% Flow: reliability
    Agent --> CB
    CB --> LLM
    Agent --> RL
    RL --> LLM

    %% Flow: agent ↔ session manager
    Agent <--> SessionMgr
    SessionMgr <--> SQLite

    %% Flow: memory
    Agent --> Consolidator
    Consolidator --> MemoryStore
    Dream --> MemoryStore
    MemoryStore <--> MemoryFiles
    MemoryStore -.-> ContextBuilder

    %% Flow: agent → outbound
    Agent --> Outbound

    %% Flow: outbound → channels
    Outbound --> WS_Channel
    Outbound --> TG_Channel

    %% Flow: cronz → scheduler → outbound
    Cronz --> Scheduler
    Scheduler -->|"reminder"| Outbound
    Scheduler -.-> SQLite

    subgraph Zero["Zero Framework"]
        WS_Channel
        TG_Channel
        Inbound
        Outbound
        Agent
        ContextBuilder
        LLM
        Tools
        SessionMgr
        SQLite
        Cronz
        Scheduler
    end

    classDef client fill:#f0f0f0,stroke:#666
    classDef zero fill:#e8f4fd,stroke:#2196F3,stroke-width:2px
    classDef data fill:#e8f5e9,stroke:#4CAF50
    classDef memory fill:#efebe9,stroke:#795548
    classDef scheduler fill:#fff8e1,stroke:#FFC107
    classDef reliability fill:#fce4ec,stroke:#E91E63

    class WS_Client,TG_Client client
    class WS_Channel,TG_Channel,Inbound,Outbound,Agent,ContextBuilder,LLM,Tools,SessionMgr zero
    class SQLite data
    class MemoryStore,Consolidator,Dream,MemoryFiles memory
    class Cronz,Scheduler scheduler
    class CB,RL reliability
Loading

See docs/ARCHITECTURE.md for a detailed walkthrough of every module.

🚀 Quick Start

# 1. Install Zig 0.15.2 and librdkafka-dev
# (see GETTING_STARTED.md for details)
zig version    # must be 0.15.1 or 0.15.2

# 2. Clone and build
git clone https://github.com/<owner>/eva.git
cd eva
zig build

# 3. Run with the bare minimum
export EVA_API_KEY="sk-your-key"
export EVA_WS_PORT=8765
./zig-out/bin/eva

# 4. Connect
wscat -c ws://localhost:8765/ws
> {"type":"message","chat_id":"u1","content":"Hi!"}

The bot responds in ~1-2 seconds (depending on LLM latency). All sessions are persisted in <workspace>/eva.db and the JSONL files under <workspace>/sessions/.

📦 Podman

podman build -t eva .
podman run -d --name eva \
  -p 8765:8765 \
  -e EVA_API_KEY="sk-..." \
  -e EVA_WS_PORT=8765 \
  -v eva-data:/app/workspace \
  eva

See GETTING_STARTED.md for the full multi-stage build explanation, Telegram setup, and Lightpanda integration.

🧪 Testing

zig build test --summary all

Expected: 32/32 tests passed.

Coverage is run with make ut locally, and on every push / PR by .github/workflows/ci.yml (using the same imng/zero-kcov:0.1 container as the zero framework). The badge above is auto-updated on PR merge.

See CONTRIBUTING.md for how to add new tests.

🔧 Configuration

All configuration is via environment variables. The complete list:

Variable Default Description
EVA_API_KEY required LLM provider API key
EVA_API_BASE https://api.openai.com OpenAI-compatible endpoint
EVA_MODEL gpt-4o-mini Default model name
EVA_WORKSPACE ~/.eva/workspace Sessions, memory files, SQLite DB
EVA_SYSTEM_PROMPT see source Initial system prompt
EVA_MAX_TOKENS 8192 Per-response token cap
EVA_CONTEXT_WINDOW 65536 Total context budget (50% triggers compact)
EVA_MAX_TOOL_ITERATIONS 10 Max LLM tool-call loops per turn
EVA_WS_PORT unset Set to enable WebSocket channel
EVA_TELEGRAM_TOKEN unset Set to enable Telegram channel
EVA_TELEGRAM_ALLOWED_USERS unset Comma-separated user IDs. Required for TG to accept any messages
EVA_CONSOLIDATE_AFTER_TURNS 20 Auto-summarize history after N turns
EVA_TG_MIN_INTERVAL_MS 500 Telegram edit-message throttle
EVA_TG_IDLE_TIMEOUT_MS 300000 Drop TG edit state after 5 min idle
EVA_SESSION_IDLE_TIMEOUT_MS 300000 Evict session locks after 5 min idle
EVA_DREAM_INTERVAL_SECONDS 1800 Background insight loop interval
EVA_CIRCUIT_BREAKER_THRESHOLD 5 Failures before circuit opens
EVA_CIRCUIT_BREAKER_COOLDOWN_MS 60000 Wait before half-open probe
EVA_RATE_LIMIT_RPM 60 Max LLM calls per minute
EVA_LIGHTPANDA_PATH lightpanda Path to lightpanda binary
EVA_WEB_FETCH_MAX_SIZE 51200 Max bytes per web_fetch response

configs/.env.sample is a complete example.

📂 Project Structure

eva/
├── src/                    # All Zig source (~7,500 LOC across 28 files)
│   ├── main.zig            # Process entry, signal handling
│   ├── root.zig            # Public API: @import("eva")
│   ├── eva.zig             # App, AppConfig, Messages, kvLog
│   ├── bus.zig             # Cross-thread message bus
│   ├── agent_loop.zig      # Main LLM loop with tool execution
│   ├── gateway.zig         # Process orchestrator
│   ├── context_builder.zig # System prompt + history + memory assembly
│   ├── session.zig         # SessionManager, SessionLock
│   ├── memory.zig          # SQLite + flat-file memory
│   ├── circuit_breaker.zig # Failure protection
│   ├── rate_limiter.zig    # Token-bucket rate limiter
│   ├── scheduler.zig       # 6-field cron with one-shots
│   ├── http_client.zig     # OpenAI-compat HTTP (Connection: close)
│   ├── tls_client.zig      # BearSSL-based TLS (Telegram long-poll)
│   ├── channel_manager.zig # Channel ↔ bus wiring
│   ├── providers/          # LLM providers (base, openai_compat, anthropic stub)
│   ├── channels/           # Channels (base, websocket, telegram, discord stub)
│   ├── tools/              # LLM-callable tools (base, registry, filesystem, shell, web)
│   └── migrations/         # SQLite schema migrations (001-006)
├── build.zig               # Build configuration (test_module pattern)
├── build.zig.zon           # Dependency manifest
├── configs/                # Sample .env files
├── scripts/                # Stress / integration test scripts
├── docs/                   # Architecture guide + assets
├── Dockerfile              # Multi-stage container (Debian → Alpine+gcompat)
├── docker-compose.yml      # Compose deployment
├── AGENTS.md               # Development guidelines (for AI agents)
├── CHANGELOG.md            # Release notes
├── GETTING_STARTED.md      # Setup walkthrough
├── LICENSE                 # Apache 2.0
├── CONTRIBUTING.md         # Contribution guide
├── CODE_OF_CONDUCT.md      # Community guidelines
├── SECURITY.md             # Vulnerability reporting
└── README.md               # This file

🛠 Development

# Run tests
zig build test --summary all

# Build with coverage
make ut

# Build optimized release
make release          # --release=fast
make release-prod     # --release=small

# Format code
zig fmt src/

# Watch tests (use entr or similar)
find src -name '*.zig' | entr -c zig build test

See CONTRIBUTING.md for the full development workflow and code style.

AI agents should read AGENTS.md.

📜 License

MIT License — see LICENSE.

🙏 Acknowledgments

See attribution.md for the full list of dependencies and asset credits.

⚠️ Trivia

This project completely built using Qwen 3.6/MiniMax M3 (Free API versions) and with little manual coding. Just for fun and personal use, nothing big to commit on.

It helped me to connect how the agentic engineering works on loop, and achieves with results. It serving well with local models (qwen 3.5 9B/Gemma 4 12B)

About

A high-performance, single-binary AI agent runtime written in Zig

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages