# Core Concepts

This guide explains the fundamental concepts of Logos ADK.

## Agent

The `Agent` is the core building block of Logos ADK. An agent combines an LLM with tools, memory, and safety mechanisms to accomplish tasks.

### Creating an Agent

```python
from logos_adk import Agent

# Basic agent
agent = Agent("assistant", model="claude-sonnet-4-20250514")

# With system prompt
agent = Agent(
    "assistant",
    model="claude-sonnet-4-20250514",
    system="You are a helpful assistant."
)

# Fluent API style
agent = (
    Agent("bot", model="gpt-4o")
    .system("You are concise.")
    .tools([...])
    .memory(...)
)
```

### Running an Agent

```python
# Synchronous run
response = await agent.run("Hello!")
print(response.content)

# With session ID for context
response = await agent.run("Continue", session_id="session-123")

# With callbacks
async def on_text(chunk):
    print(chunk.text, end="")

async def on_tool_start(tool_call):
    print(f"Calling {tool_call.name}")

response = await agent.run(
    "What's the weather?",
    on_text=on_text,
    on_tool_start=on_tool_start
)
```

### Streaming

```python
from logos_adk.types import TextChunk, ToolStart, ToolEnd, StreamDone

async for chunk in agent.stream("Tell me a story"):
    if isinstance(chunk, TextChunk):
        print(chunk.text, end="")
    elif isinstance(chunk, ToolStart):
        print(f"\n🔧 Tool: {chunk.name}")
    elif isinstance(chunk, ToolEnd):
        print(f"✓ Done: {chunk.result.content}")
    elif isinstance(chunk, StreamDone):
        print(f"\n\nTokens: {chunk.response.usage}")
```

### Structured Output

```python
from pydantic import BaseModel
from logos_adk import Agent

class Analysis(BaseModel):
    sentiment: str
    confidence: float
    keywords: list[str]

agent = (
    Agent("analyst", model="gpt-4o")
    .output_schema(Analysis)
)

response = await agent.run("Analyze: This product is amazing!")
parsed: Analysis = response.parsed
print(f"Sentiment: {parsed.sentiment}, Confidence: {parsed.confidence}")
```

## Tools

Tools extend agent capabilities by allowing them to perform actions and access external data.

### Creating Tools

```python
from logos_adk import tool

@tool
async def search(query: str) -> str:
    """Search the knowledge base."""
    results = await db.search(query)
    return format_results(results)

@tool
async def calculator(expression: str) -> str:
    """Evaluate a mathematical expression."""
    return str(eval(expression))
```

### Tool Schema

The framework automatically generates JSON schemas from function signatures:

```python
@tool
async def get_weather(
    city: str,
    units: str = "celsius",
    include_forecast: bool = False
) -> str:
    """Get weather information for a city.
    
    Args:
        city: The city name
        units: Temperature units (celsius or fahrenheit)
        include_forecast: Whether to include 7-day forecast
    """
    ...
```

Generated schema:
```json
{
  "name": "get_weather",
  "description": "Get weather information for a city.",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {"type": "string"},
      "units": {"type": "string", "default": "celsius"},
      "include_forecast": {"type": "boolean", "default": false}
    },
    "required": ["city"]
  }
}
```

### Attaching Tools

```python
# Single tool
agent.tools([search])

# Multiple tools
agent.tools([search, calculator, get_weather])

# From toolkit
from logos_adk import Toolkit
agent.tools(my_toolkit)
```

### Built-in Tool Factories

```python
from logos_adk import cli_tool, claude_code_tool, codex_tool, gemini_tool

# CLI tool execution
cli = cli_tool()
result = await cli("ls -la")

# Claude Code integration
cc = claude_code_tool()
result = await cc("Write a function that...")

# Codex integration
codex = codex_tool()
result = await codex("Generate Python code for...")

# Gemini integration
gemini = gemini_tool()
result = await gemini("Analyze this image...")
```

## Memory

Memory allows agents to maintain context across interactions.

### SessionMemory

Short-term memory that manages the conversation window:

```python
from logos_adk import SessionMemory

# Sliding window (default)
memory = SessionMemory(strategy="sliding_window", max_messages=20)

# Summarization
memory = SessionMemory(
    strategy="summarize",
    max_messages=10  # Keep last 10 messages, summarize the rest
)

# Token limit
memory = SessionMemory(
    strategy="token_limit",
    max_tokens=16000  # Keep messages under token limit
)

agent = Agent("bot", model="...").memory(memory)
```

### LongTermMemory

Persistent memory with retrieval capabilities:

```python
from logos_adk import LongTermMemory, SQLiteBackend

backend = SQLiteBackend("memory.db")
memory = LongTermMemory(
    backend=backend,
    retrieval="keyword",  # or "semantic", "hybrid"
    limit=5,
    embed_model="sentence-transformers/all-MiniLM-L6-v2"
)

agent = Agent("bot", model="...").memory(memory)
```

### SharedMemory

Memory shared across sessions and agents:

```python
from logos_adk import SharedMemory, SQLiteBackend

backend = SQLiteBackend("shared.db")
memory = SharedMemory(
    backend=backend,
    retrieval="hybrid",
    limit=10
)

# All agents with this memory share the same knowledge
agent1 = Agent("agent1", model="...").memory(memory)
agent2 = Agent("agent2", model="...").memory(memory)
```

### Memory Retrieval Strategies

| Strategy | Description |
|----------|-------------|
| `keyword` | BM25 keyword matching |
| `semantic` | Vector similarity search |
| `hybrid` | Combined keyword + semantic |

## Guardrails

Guardrails validate and filter input/output content.

### ContentFilter

Block specific words or phrases:

```python
from logos_adk import ContentFilter

filter = ContentFilter(
    blocked=["spam", "abuse", "inappropriate"],
    case_sensitive=False,
    action="block"  # or "warn", "override"
)

agent = Agent("bot", model="...").input_guardrail(filter)
```

### RegexGuard

Pattern-based validation:

```python
from logos_adk import RegexGuard

# Block email patterns
guard = RegexGuard(
    deny=[r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"],
    action="block",
    message="Email addresses are not allowed"
)

# Allow only specific patterns
guard = RegexGuard(
    allow=[r"^[A-Za-z\s]+$"],  # Only letters and spaces
    action="block"
)
```

### MaxLengthGuard

Limit content length:

```python
from logos_adk import MaxLengthGuard

guard = MaxLengthGuard(
    max_chars=1000,
    action="block"
)
```

### KeywordGuard

Flexible keyword detection:

```python
from logos_adk import KeywordGuard

guard = KeywordGuard(
    keywords=["competitor", "rival"],
    action="warn",  # Log warning but continue
    message="Competitor mention detected",
    case_sensitive=False
)
```

### Guard Actions

| Action | Behavior |
|--------|----------|
| `block` | Raise `GuardrailError`, stop execution |
| `warn` | Log warning, continue execution |
| `override` | Replace content with `override_with` value |

## RAG (Retrieval-Augmented Generation)

RAG enables agents to access external knowledge bases.

### Loading Documents

```python
from logos_adk.rag import TextLoader, MarkdownLoader, DocumentLoader

# Load text files
loader = TextLoader("docs/texts/")
documents = loader.load()

# Load markdown files
md_loader = MarkdownLoader("docs/md/")
documents = md_loader.load()

# Custom loader
class CustomLoader(DocumentLoader):
    def load(self, path: str) -> list[Document]:
        ...
```

### Splitting Documents

```python
from logos_adk.rag import RecursiveCharacterSplitter

splitter = RecursiveCharacterSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", ". ", " "]
)

chunks = splitter.split(documents)
```

### Vector Stores

```python
from logos_adk.rag import InMemoryVectorStore, KnowledgeBase

# In-memory store
async def embed_fn(text: str) -> list[float]:
    # Your embedding function
    return [0.1, 0.2, ...]

store = InMemoryVectorStore(embed_fn=embed_fn)
store.add_documents(documents, splitter)

# Create knowledge base
kb = KnowledgeBase(store)

# Attach to agent
agent = (
    Agent("expert", model="...")
    .knowledge(kb, mode="memory", limit=5)
)
```

### Knowledge Base Modes

| Mode | Description |
|------|-------------|
| `memory` | Auto-inject context into prompts |
| `tool` | Agent explicitly searches via tool |
| `both` | Both memory injection and tool |

### RAG Tool

```python
from logos_adk.rag import rag_tool

rag = rag_tool(kb, limit=5)
agent = Agent("researcher", model="...").tools([rag])
```

## MCP (Model Context Protocol)

MCP allows connecting to external tool servers.

### Connection Types

```python
from logos_adk import Agent, MCPClient

# SSE (Server-Sent Events) connection
agent = Agent("bot", model="...").mcp("http://localhost:3000/sse")

# stdio connection
agent = Agent("bot", model="...").mcp("npx @server-github")

# Direct client
client = MCPClient.from_string("http://localhost:3000")
agent = Agent("bot", model="...").mcp(client)
```

### MCPClient

```python
from logos_adk import MCPClient

client = MCPClient("http://localhost:3000/sse")
await client.connect()

# Get available tools
tools = client.get_tools()

# Use in agent
agent = Agent("bot", model="...").mcp(client)
```

## Teams

Teams enable multi-agent collaboration.

### Creating a Team

```python
from logos_adk import Agent, Team

researcher = Agent("researcher", model="...", system="Research facts.")
writer = Agent("writer", model="...", system="Write content.")
editor = Agent("editor", model="...", system="Edit and review.")

team = Team(
    "content_team",
    agents=[researcher, writer, editor],
    orchestrator="researcher",
    topology="mesh",
    max_rounds=10
)

response = await team.run("Write an article about AI")
```

### Topology Types

| Topology | Description |
|----------|-------------|
| `mesh` | All agents can communicate with all |
| `star` | Central hub agent routes messages |
| `line` | Sequential chain communication |
| Custom | Define explicit routing |

### Custom Topology

```python
topology = {
    "researcher": ["writer", "editor"],
    "writer": ["editor"],
    "editor": []
}

team = Team(
    "team",
    agents=[...],
    topology=topology
)
```

### Agent Communication

```python
# Send to specific agent
await agent.send_to("writer", "Here are the facts: ...")

# Broadcast to all allowed agents
await agent.broadcast_msg("Task completed!")

# Handle messages with @handler
from logos_adk import handler

class ResearchAgent:
    @handler
    def handle_research_request(self, msg: ResearchRequest):
        return f"Researching: {msg.topic}"
```

## Observability

Track agent execution with spans and metrics.

### Exporters

```python
from logos_adk import (
    ConsoleExporter,
    InMemoryExporter,
    OpenTelemetryExporter
)

# Console output
console = ConsoleExporter()

# In-memory (for testing)
memory = InMemoryExporter()

# OpenTelemetry
otel = OpenTelemetryExporter(
    endpoint="http://localhost:4318/v1/traces",
    headers={"x-api-key": "..."},
    compression="gzip"
)

agent = Agent("bot", model="...").observe(console, otel)
```

### Span Data

Each agent turn generates spans with:
- `trace_id` — Unique trace identifier
- `span_id` — Unique span identifier
- `parent_span_id` — Parent span for hierarchy
- `duration_ms` — Execution time
- `attributes` — Custom metadata
- `status` — ok/error/warning

### Metrics

```python
from logos_adk.observe import Metric

# Custom metrics
metric = Metric(
    name="custom.metric",
    value=42.0,
    unit="ms",
    attributes={"agent": "bot"}
)
```

## Caching

Cache responses to reduce latency and costs.

### Basic Caching

```python
from logos_adk import Agent, InMemoryCacheBackend

backend = InMemoryCacheBackend()
agent = (
    Agent("bot", model="...")
    .cache(backend, ttl=3600)  # 1 hour TTL
)
```

### Semantic Caching

```python
async def embed_fn(text: str) -> list[float]:
    return await your_embedding_model(text)

backend = InMemoryCacheBackend()
agent = (
    Agent("bot", model="...")
    .cache(
        backend,
        ttl=3600,
        semantic=True,
        embed_fn=embed_fn,
        similarity_threshold=0.95
    )
)
```

### SQLite Cache

```python
from logos_adk import SQLiteCacheBackend

backend = SQLiteCacheBackend("cache.db")
agent = Agent("bot", model="...").cache(backend, ttl=7200)
```

## Error Handling

### GuardrailError

```python
from logos_adk import GuardrailError, Agent

try:
    response = await agent.run("prompt with blocked content")
except GuardrailError as e:
    print(f"Blocked: {e.result.message}")
```

### ConfigError

```python
from logos_adk.errors import ConfigError

try:
    agent = Agent("bot", model="invalid-model")
    await agent.run("test")
except ConfigError as e:
    print(f"Configuration error: {e}")
```

### MaxRoundsExceeded

```python
from logos_adk.errors import MaxRoundsExceeded

try:
    response = await team.run_rounds()
except MaxRoundsExceeded as e:
    print(f"Team exceeded max rounds: {e}")
```

## Hot Reload

Watch for config changes and reload automatically:

```python
from logos_adk import ConfigWatcher

async def on_reload(config):
    print(f"Config reloaded: {config.name}")

watcher = ConfigWatcher("agent.yaml", on_reload=on_reload)
await watcher.start()
```

## Cost Tracking

Track token usage and costs:

```python
from logos_adk import CostTracker

tracker = CostTracker()

# Track per agent
agent = Agent("bot", model="...")
response = await agent.run("test")
report = tracker.get_report(agent)

print(f"Input tokens: {report.input_tokens}")
print(f"Output tokens: {report.output_tokens}")
print(f"Estimated cost: ${report.estimated_cost}")
```
