# novyx-agent

Build AI agents with persistent memory, rollback, and audit trails.

```python
from novyx_agent import Agent, tool

agent = Agent(name="Aria", api_key="nram_...")

@agent.tool
def search_web(query: str) -> str:
    """Search the web for information."""
    return requests.get(f"https://api.search.com?q={query}").text

result = agent.run("What did we discuss yesterday?")
print(result)
```

**That's it.** The agent automatically recalls relevant memories before every LLM call, executes tools, and remembers the exchange for next time.

## Why novyx-agent?

Every agent framework gives you tool calling. **None of them give you this:**

```python
# Roll back the agent's memory to before it went off the rails
agent.rollback("2 hours ago")

# Cryptographic audit trail — every memory operation, hash-chained
trail = agent.audit(limit=50)

# Memory health check — recall accuracy, drift, conflicts, staleness
health = agent.eval()
assert health["score"] >= 0.8, "Memory quality too low"

# Pass/fail gate for CI/CD
agent.eval_gate(min_score=0.7)

# Knowledge graph
agent.add_triple("Python", "is_a", "programming language")
triples = agent.query_triples(subject="Python")
```

No other agent SDK has rollback, audit trails, eval, or replay. Not OpenAI Agents SDK, not CrewAI, not LangGraph, not Pydantic AI.

## Install

```bash
pip install novyx-agent

# Pick your LLM provider:
pip install novyx-agent[openai]      # OpenAI / OpenAI-compatible
pip install novyx-agent[anthropic]   # Claude
pip install novyx-agent[litellm]     # Any model via LiteLLM
pip install novyx-agent[all]         # All providers
```

## Quick Start

### 1. Basic agent (5 lines)

```python
from novyx_agent import Agent

agent = Agent(
    name="Assistant",
    api_key="nram_your_key",
    model="gpt-4o-mini",
)

result = agent.run("Summarize what we've been working on")
print(result)
```

### 2. Agent with tools

```python
from novyx_agent import Agent, tool

agent = Agent(name="ResearchBot", api_key="nram_...")

@agent.tool
def search_docs(query: str, limit: int = 5) -> str:
    """Search internal documentation."""
    # Your search logic here
    return f"Found {limit} results for: {query}"

@agent.tool
def create_ticket(title: str, body: str) -> str:
    """Create a support ticket."""
    return f"Created ticket: {title}"

result = agent.run("Find docs about auth and create a ticket for the bug")
print(result)
print(f"Tools used: {[tc.tool for tc in result.tool_calls]}")
```

### 3. Agent with Claude

```python
agent = Agent(
    name="Claude Agent",
    api_key="nram_...",
    model="claude-sonnet-4-20250514",
    provider="anthropic",
)
```

### 4. Agent with any model (LiteLLM)

```python
agent = Agent(
    name="Universal",
    api_key="nram_...",
    model="ollama/llama3",      # or "groq/llama3-70b", "together/mistral-7b", etc.
    provider="litellm",
)
```

### 5. Memory control

```python
from novyx_agent import Agent, MemoryConfig

agent = Agent(
    name="Selective",
    api_key="nram_...",
    memory=MemoryConfig(
        auto_recall=True,           # Recall before each run
        auto_remember=True,         # Remember after each run
        recall_limit=10,            # Max memories to recall
        recall_threshold=0.5,       # Minimum relevance score
        recall_tags=["project-x"],  # Only recall tagged memories
        remember_tags=["project-x"],# Tag stored memories
        remember_importance=8,      # 1-10 importance
    ),
)
```

### 6. Rollback & audit

```python
# Oops, the agent stored bad data
agent.rollback("30 minutes ago")

# Preview before committing
preview = agent.rollback_preview("1 hour ago")
print(f"Would restore {preview['restore_count']} memories")

# Full audit trail
for entry in agent.audit(limit=20):
    print(f"{entry['action']} at {entry['timestamp']}")
```

### 7. Eval gates for CI/CD

```python
health = agent.eval()
print(f"Memory score: {health['score']}")
# score = 0.7*recall + 0.2*freshness + 0.1*consistency

# Hard gate — raises if below threshold
agent.eval_gate(min_score=0.7)
```

### 8. Direct Novyx access

```python
# Full SDK access for advanced operations
nx = agent.novyx
nx.dashboard()
nx.cortex_run()
nx.replay_timeline(hours=24)
```

## Architecture

```
novyx-agent
├── agent.py      — Agent class (run loop, memory integration)
├── tools.py      — @tool decorator (auto-schema from type hints)
├── types.py      — Pydantic models (ToolDef, RunResult, configs)
├── providers.py  — LLM adapters (OpenAI, Anthropic, LiteLLM)
└── __init__.py   — Public API
```

The run loop:

1. **Recall** — semantic search over Novyx memories relevant to the prompt
2. **Build context** — system prompt + recalled memories + conversation history + user message
3. **LLM loop** — call the model → if tool calls, execute them → repeat until text response
4. **Remember** — store a summary of the exchange as a new memory
5. **Return** — `RunResult` with output, tool calls, memory stats, token usage

## API Reference

### `Agent`

| Method | Description |
|---|---|
| `run(prompt)` | Run the agent on a prompt |
| `remember(observation)` | Store a memory |
| `recall(query)` | Search memories |
| `forget(memory_id)` | Delete a memory |
| `rollback(target)` | Roll back memory state |
| `rollback_preview(target)` | Preview rollback |
| `audit(**kwargs)` | Get audit trail |
| `eval()` | Run memory health eval |
| `eval_gate(min_score)` | Pass/fail gate |
| `add_triple(s, p, o)` | Add knowledge graph triple |
| `query_triples(**kwargs)` | Query knowledge graph |
| `clear_history()` | Clear conversation history |
| `novyx` | Direct Novyx SDK access |

### `@tool`

```python
@tool
def my_function(param: str, count: int = 5) -> str:
    """Description becomes the tool description."""
    return "result"
```

Automatically extracts: name, description (from docstring), parameters (from type hints), required/optional (from defaults).

### `RunResult`

| Field | Type | Description |
|---|---|---|
| `output` | `str` | The agent's text response |
| `tool_calls` | `List[ToolCall]` | Tools invoked during the run |
| `memories_recalled` | `int` | Memories retrieved |
| `memories_stored` | `int` | Memories created |
| `model` | `str` | Model used |
| `usage` | `Dict[str, int]` | Token usage |

## License

MIT
