Skip to main content

Choosing the Right Primitive

This guide helps you decide which Grid primitives to use based on your specific needs and use case.

Quick Decision Tree​

graph TD
A[What are you building?] --> B{Do you need agent/LLM integration?}
B -->|Yes| C{Do you need tool execution?}
B -->|No| D{Do you need both history and state?}

C -->|Yes| E[Use ConversationLoop]
C -->|No| F{Custom agent integration?}

F -->|Yes| G[Use Atomic Primitives]
F -->|No| H[Use ConversationManager + Agent]

D -->|Yes| I{Want convenience methods?}
D -->|No| J{Just messages or just state?}

I -->|Yes| K[Use ConversationManager]
I -->|No| L[Use Both Atomic Primitives]

J -->|Messages| M[Use ConversationHistory]
J -->|State| N[Use ConversationContext]

Detailed Comparison​

Atomic Primitives​

ConversationHistory​

Use when you need:

  • ✅ Just message storage
  • ✅ Simple chat logging
  • ✅ Audit trails
  • ✅ Maximum flexibility

Best for:

  • Logging systems
  • Simple chatbots
  • Message archives
  • Custom implementations
const history = createConversationHistory();
// Perfect for simple message tracking

ConversationContext​

Use when you need:

  • ✅ Just state management
  • ✅ User preferences storage
  • ✅ Session tracking
  • ✅ Analytics/metrics

Best for:

  • State machines
  • User preference systems
  • Analytics tracking
  • Session management
const context = createConversationContext();
// Ideal for managing conversation state

Composed Layer​

ConversationManager​

Use when you need:

  • ✅ Both history AND context
  • ✅ Convenience methods
  • ✅ Unified interface
  • ✅ No agent integration yet

Best for:

  • Standard chat applications
  • Customer support systems
  • Multi-modal conversations
  • Prototyping
const manager = createConversationManager();
// Combines both primitives with helpful methods

Organism Layer​

ConversationLoop​

Use when you need:

  • ✅ Full agent integration
  • ✅ Automatic tool handling
  • ✅ Progress tracking
  • ✅ Lifecycle management

Best for:

  • Production AI applications
  • Complex workflows
  • Tool-enabled agents
  • Real-time systems
const loop = createConversationLoop({ agent });
// Complete solution with agent orchestration

Use Case Examples​

Use Case 1: Simple Chatbot​

Requirements:

  • Store conversation history
  • No complex state needed
  • No AI/LLM integration

Recommendation: Use ConversationHistory only

const history = createConversationHistory(
"Welcome to our support chat!"
);

async function handleMessage(userMsg: string) {
await history.addMessage({ role: "user", content: userMsg });

const response = getSimpleResponse(userMsg);
await history.addMessage({ role: "assistant", content: response });
}

Use Case 2: Stateful Form Flow​

Requirements:

  • Track user progress
  • Store form data
  • No conversation history needed

Recommendation: Use ConversationContext only

const context = createConversationContext();

async function handleFormStep(field: string, value: any) {
await context.updateState(`form.${field}`, value);

const progress = calculateProgress(context.getState());
await context.updateMetadata('progress', progress);
}

Use Case 3: Customer Support System​

Requirements:

  • Full conversation history
  • Track ticket status
  • Store customer info
  • No AI agent (human agents)

Recommendation: Use ConversationManager

const manager = createConversationManager();

// Track conversation and ticket state together
await manager.addUserMessage("I need help with my order");
await manager.updateState("ticket.status", "open");
await manager.updateState("ticket.priority", "high");

Use Case 4: AI-Powered Assistant​

Requirements:

  • LLM integration
  • Tool execution
  • Full conversation tracking
  • Real-time updates

Recommendation: Use ConversationLoop

const loop = createConversationLoop({
agent: myAgent,
toolExecutor: executor,
onProgress: (update) => sendToWebSocket(update)
});

const response = await loop.sendMessage("Help me plan a trip");

Use Case 5: Custom Analytics System​

Requirements:

  • Track specific metrics
  • Custom state structure
  • Separate from main conversation
  • Complex calculations

Recommendation: Use atomic primitives with custom logic

// Main conversation
const conversation = createConversationManager();

// Separate analytics tracking
const analyticsHistory = createConversationHistory();
const analyticsContext = createConversationContext();

class AnalyticsTracker {
async trackInteraction(input: string, output: string) {
// Custom analytics logic
const sentiment = analyzeSentiment(input);
const topics = extractTopics(input);

await analyticsContext.updateStates({
'metrics.sentiment.current': sentiment,
'metrics.topics': topics
});

await analyticsHistory.addMessage({
role: "system",
content: `Analytics: sentiment=${sentiment}, topics=${topics.join(',')}`
});
}
}

Performance Considerations​

Memory Usage​

PrimitiveMemory ImpactScaling
ConversationHistoryLinear with messagesUse maxMessages to limit
ConversationContextDepends on state sizeMinimal, key-value pairs
ConversationManagerCombinedSum of both primitives
ConversationLoopHighestIncludes agent state

Complexity vs Features​

Simple                                              Complex
←─────────────────────────────────────────────────→
History │ Context │ Manager │ Loop │ Custom

Fewer features More features
Better control More convenience

Migration Paths​

Bottom-Up Migration​

Start simple and add layers as needed:

// Phase 1: Just history
const history = createConversationHistory();

// Phase 2: Add context when needed
const context = createConversationContext();

// Phase 3: Upgrade to manager
const manager = createConversationManager();
// Can still access: manager.history, manager.context

// Phase 4: Add full orchestration
const loop = createConversationLoop({ agent });
// Can still access: loop.manager.history

Top-Down Approach​

Start with everything and access lower layers:

// Start with full loop
const loop = createConversationLoop({ agent });

// But can still access primitives directly
const messages = loop.manager.history.getMessages();
const state = loop.manager.context.getState();

// Or bypass manager entirely
loop.getMessages(); // Direct access

Decision Matrix​

FeatureHistoryContextManagerLoop
Message Storage✅❌✅✅
State Management❌✅✅✅
Event Handlers✅✅✅✅
Convenience Methods❌❌✅✅
Agent Integration❌❌❌✅
Tool Execution❌❌❌✅
Progress Tracking❌❌❌✅
Lifecycle Hooks❌❌❌✅

Common Patterns​

Pattern 1: Separation of Concerns​

Use different primitives for different aspects:

// User-facing conversation
const userConversation = createConversationManager();

// Internal system logs
const systemLogs = createConversationHistory();

// Performance metrics
const metrics = createConversationContext();

Pattern 2: Gradual Complexity​

Add features incrementally:

// Start: Simple logging
let solution: any = createConversationHistory();

// Growth: Need state
solution = {
history: solution,
context: createConversationContext()
};

// More growth: Want convenience
solution = createConversationManager();

// Final: Need agents
solution = createConversationLoop({ agent });

Pattern 3: Hybrid Approach​

Mix levels based on requirements:

class HybridSystem {
// Use loop for main flow
private mainFlow = createConversationLoop({ agent });

// Use raw primitives for special needs
private debugLog = createConversationHistory();
private cache = createConversationContext();

async process(input: string) {
// Debug logging
await this.debugLog.addMessage({
role: "system",
content: `Processing: ${input}`
});

// Check cache
const cached = this.cache.getStateValue(`cache.${hash(input)}`);
if (cached) return cached;

// Main processing
const result = await this.mainFlow.sendMessage(input);

// Update cache
await this.cache.updateState(`cache.${hash(input)}`, result);

return result;
}
}

Recommendations by Team Size​

Solo Developer​

  • Start with atomic primitives
  • Build exactly what you need
  • Migrate up as requirements grow

Small Team​

  • Use ConversationManager for standardization
  • Add ConversationLoop when adding AI
  • Keep custom logic minimal

Large Team​

  • Define clear interfaces
  • Use ConversationLoop for consistency
  • Build abstractions on top
  • Document your patterns

Future Considerations​

When choosing primitives, consider:

  1. Future Requirements

    • Will you need agents later?
    • Might you add tools?
    • Could state management grow complex?
  2. Integration Needs

    • Database persistence?
    • Real-time updates?
    • Analytics requirements?
  3. Performance Requirements

    • Message volume?
    • State size?
    • Response latency?

Summary​

  • Need just messages? → ConversationHistory
  • Need just state? → ConversationContext
  • Need both without agents? → ConversationManager
  • Need everything? → ConversationLoop
  • Need custom behavior? → Mix and match!

Remember: Grid's architecture lets you start anywhere and evolve as needed. There's no wrong choice - only what fits your current needs best.

Next Steps​