# ADR-006: Rig Framework para LLM Agent Orchestration

**Status**: Accepted | Implemented
**Date**: 2024-11-01
**Deciders**: LLM Architecture Team
**Technical Story**: Selecting Rust-native framework for LLM agent tool calling and streaming

---

## Decision

Usar **rig-core 0.15** para orquestación de agentes LLM (no LangChain, no SDKs directos de proveedores).

---

## Rationale

1. **Rust-Native**: Sin dependencias Python, compila a binario standalone
2. **Tool Calling Support**: First-class abstraction para function calling
3. **Streaming**: Built-in streaming de respuestas
4. **Minimal Abstraction**: Wrapper thin sobre APIs de proveedores (no over-engineering)
5. **Type Safety**: Schemas automáticos para tool definitions

---

## Alternatives Considered

### ❌ LangChain (Python Bridge)
- **Pros**: Muy maduro, mucho tooling
- **Cons**: Requiere Python runtime, complejidad de IPC

### ❌ Direct Provider SDKs (Claude, OpenAI, etc.)
- **Pros**: Control total
- **Cons**: Reimplementar tool calling, streaming, error handling múltiples veces

### ✅ Rig Framework (CHOSEN)
- Rust-native, thin abstraction
- Tool calling built-in
- Streaming support

---

## Trade-offs

**Pros**:
- ✅ Rust-native (no Python dependency)
- ✅ Tool calling abstraction reducida
- ✅ Streaming responses
- ✅ Type-safe schemas
- ✅ Minimal memory footprint

**Cons**:
- ⚠️ Comunidad más pequeña que LangChain
- ⚠️ Menos ejemplos/tutorials disponibles
- ⚠️ Actualización menos frecuente que alternatives

---

## Implementation

**Agent with Tool Calling**:
```rust
// crates/vapora-llm-router/src/providers.rs
use rig::client::Client;
use rig::completion::Prompt;

let client = rig::client::OpenAIClient::new(&api_key);

// Define tool schema
let calculate_tool = rig::tool::Tool {
    name: "calculate".to_string(),
    description: "Perform arithmetic calculation".to_string(),
    schema: json!({
        "type": "object",
        "properties": {
            "expression": {"type": "string"}
        }
    }),
};

// Call with tool
let response = client
    .post_chat()
    .preamble("You are a helpful assistant")
    .user_message("What is 2 + 2?")
    .tool(calculate_tool)
    .call()
    .await?;
```

**Streaming Responses**:
```rust
// Stream chunks as they arrive
let mut stream = client
    .post_chat()
    .user_message(prompt)
    .stream()
    .await?;

while let Some(chunk) = stream.next().await {
    match chunk {
        Ok(text) => println!("{}", text),
        Err(e) => eprintln!("Error: {:?}", e),
    }
}
```

**Key Files**:
- `/crates/vapora-llm-router/src/providers.rs` (provider implementations)
- `/crates/vapora-llm-router/src/router.rs` (routing logic)
- `/crates/vapora-agents/src/executor.rs` (agent task execution)

---

## Verification

```bash
# Test tool calling
cargo test -p vapora-llm-router test_tool_calling

# Test streaming
cargo test -p vapora-llm-router test_streaming_response

# Integration test with real provider
cargo test -p vapora-llm-router test_agent_execution -- --nocapture

# Benchmark tool calling latency
cargo bench -p vapora-llm-router bench_tool_response_time
```

**Expected Output**:
- Tools invoked correctly with parameters
- Streaming chunks received in order
- Agent executes tasks and returns results
- Latency < 100ms per tool call

---

## Consequences

### Developer Workflow
- Tool schemas defined in code (type-safe)
- No Python bridge debugging complexity
- Single-language stack (all Rust)

### Performance
- Minimal latency (direct to provider APIs)
- Streaming reduces perceived latency
- Tool calling has <50ms overhead

### Future Extensibility
- Adding new providers: implement `LLMClient` trait
- Custom tools: define schema + handler in Rust
- See ADR-007 (Multi-Provider Support)

---

## References

- [Rig Framework Documentation](https://github.com/0xPlaygrounds/rig)
- `/crates/vapora-llm-router/src/providers.rs` (provider abstractions)
- `/crates/vapora-agents/src/executor.rs` (agent execution)

---

**Related ADRs**: ADR-007 (Multi-Provider LLM), ADR-001 (Workspace)
