CommBus

Conversational context assembly for LLM interactions. CommBus injects keyword-triggered entries into prompts while respecting token budgets, providing intelligent context management for your AI applications.

Features

Installation

Add comm_bus to your list of dependencies in mix.exs:

def deps do
  [
    {:comm_bus, "~> 0.1"}
  ]
end

Quick Start

Basic Assembly

alias CommBus.{Assembler, Conversation, Entry, Message}

# Define conversation
conversation = %Conversation{
  messages: [
    %Message{role: :user, content: "Need help with authentication."}
  ]
}

# Define context entries
entries = [
  # Constant entry - always included
  %Entry{
    id: "system-rules",
    mode: :constant,
    section: :system,
    token_count: 15,
    content: "You are a helpful assistant. Be concise and accurate.",
    priority: 100
  },

  # Triggered entry - only when keywords match
  %Entry{
    id: "auth-context",
    mode: :triggered,
    keywords: ["auth*", "login", "session"],
    section: :pre_history,
    token_count: 50,
    content: "Authentication system uses JWT tokens with 24h expiration...",
    priority: 80
  }
]

# Assemble with budget
result = Assembler.assemble_prompt(
  conversation,
  entries,
  budget: %{
    total: 1000,
    sections: %{
      system: 100,
      pre_history: 300,
      history: 400,
      post_history: 200
    }
  }
)

# Result contains sectioned messages ready for LLM
IO.inspect(result.messages)
IO.inspect(result.token_usage)

Using Methodologies

Methodologies are curated prompt packs for common workflows:

# Load methodology entries
entries = CommBus.Methodologies.entries_for("bug_triage")

# Or load specific entry
entries = CommBus.Methodologies.entries_for("bug_triage#severity-check")

# Combine with custom entries
all_entries = entries ++ [
  %Entry{id: "project-context", mode: :constant, content: "Project: MyApp..."}
]

# Assemble as usual
Assembler.assemble_prompt(conversation, all_entries)

With Storage Adapter

# Configure in config/config.exs
config :comm_bus,
  storage: CommBus.Storage.EctoAdapter,
  repo: MyApp.Repo

# Use in application
{:ok, conversation} = CommBus.Storage.get_conversation(conv_id)
{:ok, entries} = CommBus.Storage.list_entries(filters: [enabled: true])

result = Assembler.assemble_prompt(conversation, entries)

With llm_core Integration

alias CommBus.Protocol.LlmCoreAdapter

# 1. Assemble context
packet = Assembler.assemble_prompt(conversation, entries)

# 2. Convert to llm_core format
{:ok, llm_messages} = LlmCoreAdapter.to_provider_messages(packet)

# 3. Send to LLM via llm_core
{:ok, response} = LlmCore.complete(
  llm_messages,
  model: "gpt-4",
  provider: :openai
)

Mix Tasks

CommBus provides several CLI utilities for development and testing.

List Entries

View entries from any storage adapter:

# List all entries from InMemory storage
mix comm_bus.entries --store InMemory

# Filter by mode
mix comm_bus.entries --store InMemory --mode triggered

# Filter by enabled state
mix comm_bus.entries --store InMemory --enabled true

Budget Simulation

Simulate token budget allocation:

mix comm_bus.budget \
  --conversation test/fixtures/conversations/basic.yml \
  --entries test/fixtures/entries/sample.yml \
  --total 1000

Simulate Assembly

Full end-to-end assembly simulation:

mix comm_bus.simulate \
  --conversation test/fixtures/conversations/basic.yml \
  --entries test/fixtures/entries/sample.yml \
  --budget 2000

Compare Template Engines

Compare BbMustache vs ExMustache rendering:

mix comm_bus.compare_engines

Sync Test Fixtures

Synchronize golden template fixtures:

mix comm_bus.sync_fixtures

Methodologies

CommBus includes a methodology system for reusable prompt packs.

Built-in Methodologies

Using Methodologies

# Get all entries from a methodology
entries = CommBus.Methodologies.entries_for("bug_triage")

# Get specific entry by fragment identifier
entries = CommBus.Methodologies.entries_for("bug_triage#step-1")

# Load multiple methodologies
entries = CommBus.Methodologies.entries_for([
  "bug_triage",
  "root_cause#analysis"
])

# List available methodologies
methodologies = CommBus.Methodologies.list()

Creating Custom Methodologies

Place YAML files in config/comm_bus/methodologies/:

name: "API Development"
description: "Context entries for API development workflows"
slug: "api-dev"
tags: ["api", "rest", "development"]
entries:
  - id: "rest-principles"
    content: |
      REST API Design Principles:
      - Use HTTP methods correctly (GET, POST, PUT, DELETE)
      - Resource-based URLs
      - Stateless communication
    keywords: ["api", "rest", "endpoint"]
    section: pre_history
    mode: triggered
    priority: 80
    weight: 1.0

  - id: "error-handling"
    content: "Always return appropriate HTTP status codes..."
    keywords: ["error", "status", "response"]
    section: pre_history
    mode: triggered
    priority: 70

Configuration

Basic Configuration

# config/config.exs
config :comm_bus,
  # Template engine (BbMustache is default)
  template_engine: CommBus.Template.Engine.BbMustache,

  # Tokenizer implementation
  tokenizer: CommBus.Tokenizer.Simple,

  # Prompt directory
  prompt_root: Path.expand("config/comm_bus/prompts", File.cwd!()),

  # Methodology directory
  methodology_root: Path.expand("config/comm_bus/methodologies", File.cwd!()),

  # Storage adapter
  storage: CommBus.Storage.InMemory

Storage Configuration

In-Memory (Development/Testing)

config :comm_bus,
  storage: CommBus.Storage.InMemory

Ecto Adapter (Production)

config :comm_bus,
  storage: CommBus.Storage.EctoAdapter,
  repo: MyApp.Repo

You can implement custom storage adapters by adopting the CommBus.Storage behaviour.

Architecture

┌─────────────────┐
│  Application    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐     ┌──────────────┐
│   CommBus       │────▶│  Storage     │
│   Assembler     │     │  Adapter     │
└────────┬────────┘     └──────────────┘
         │
         ▼
┌─────────────────┐
│   Matcher       │ ─── Keyword matching
└─────────────────┘

┌─────────────────┐
│   Budget        │ ─── Token fitting
└─────────────────┘

┌─────────────────┐
│   Protocol      │ ─── llm_core adapter
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   LLM Provider  │
└─────────────────┘

Key Components

Data Structures

Entry

Injectable context with metadata:

%CommBus.Entry{
  id: "unique-id",
  content: "Context information...",
  keywords: ["keyword1", "key*", "\"exact phrase\""],
  section: :pre_history,        # :system | :pre_history | :history | :post_history
  mode: :triggered,              # :constant | :triggered
  priority: 80,                  # Higher = more important
  weight: 1.0,                   # Multiplier for token budget
  token_count: 50,
  metadata: %{}
}

Message

OpenAI-compatible message format:

%CommBus.Message{
  role: :user,                   # :system | :user | :assistant | :tool
  content: "Message text...",
  token_count: 10,
  metadata: %{}
}

Conversation

Session state with message history:

%CommBus.Conversation{
  id: "conv-123",
  messages: [%Message{}, ...],
  depth: 3,                      # Number of exchanges
  metadata: %{}
}

Packet

Assembly output ready for LLM:

%CommBus.Protocol.Packet{
  messages: [%Message{}, ...],   # Sectioned and assembled
  sections: %{                   # Messages grouped by section
    system: [...],
    pre_history: [...],
    history: [...],
    post_history: [...]
  },
  included_entries: [...],       # Entries that fit in budget
  excluded_entries: [...],       # Entries that didn't fit
  token_usage: %{                # Token accounting
    total: 950,
    by_section: %{...}
  }
}

Testing

# Run all tests
mix test

# Run with coverage
mix test --cover

# Run specific test file
mix test test/comm_bus/assembler_test.exs

# Run Mix task tests
mix test test/mix/tasks/

# Compare template engines
mix comm_bus.compare_engines

# Sync golden fixtures
mix comm_bus.sync_fixtures

Documentation

Generate and view documentation locally:

mix docs
open doc/index.html

Related Projects

License

MIT — see LICENSE.