Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/.codebase-info/.map-state.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"tool": "codebase-mapper",
"version": "2.0.0",
"mappedAt": "2026-06-13",
"gitCommit": "3d33e62f2b6796b7259c1e3e5d77c4313a56f22d",
"mappedAt": "2026-07-05",
"gitCommit": "28099f5aa6eccb491c66be4bcd22532f15bb407f",
"documents": [
"architecture.md",
"tech-landscape.md",
Expand Down
4 changes: 2 additions & 2 deletions .claude/.codebase-info/architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Architecture

*Last Updated: 2026-06-13*
*Last Updated: 2026-07-05*

## Summary

Expand Down Expand Up @@ -47,7 +47,7 @@ Tool install (atomic-assembler TUI):
|-----------|------|----------------|
| Core agent | `atomic-agents/atomic_agents/agents/atomic_agent.py` | `AtomicAgent`, `AgentConfig`, run / stream / async methods |
| Base contracts | `atomic-agents/atomic_agents/base/` | `BaseIOSchema`, `BaseTool`, `BaseToolConfig`, `BaseResource`, `BasePrompt` |
| Context | `atomic-agents/atomic_agents/context/` | `SystemPromptGenerator`, `BaseDynamicContextProvider`, `ChatHistory` |
| Context | `atomic-agents/atomic_agents/context/` | `SystemPromptGenerator`, `BaseDynamicContextProvider`, `BaseChatHistory` (pluggable memory contract) + `ChatHistory` |
| Connectors | `atomic-agents/atomic_agents/connectors/mcp/` | Model Context Protocol tools / resources / prompts |
| Utils | `atomic-agents/atomic_agents/utils/` | Token counting (LiteLLM), tool-message formatting |
| Assembler (CLI) | `atomic-assembler/atomic_assembler/` | Textual TUI to fetch/install forge tools |
Expand Down
12 changes: 6 additions & 6 deletions .claude/.codebase-info/directory-structure.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Directory Structure

*Last Updated: 2026-06-13*
*Last Updated: 2026-07-05*

## Root Layout

Expand All @@ -10,7 +10,7 @@ atomic-agents/ # repo root (uv workspace)
│ └── atomic_agents/ # import package
│ ├── agents/ # AtomicAgent, AgentConfig
│ ├── base/ # BaseIOSchema, BaseTool, BaseResource, BasePrompt
│ ├── context/ # SystemPromptGenerator, ChatHistory, context providers
│ ├── context/ # SystemPromptGenerator, BaseChatHistory/ChatHistory, context providers
│ ├── connectors/mcp/ # Model Context Protocol integration
│ └── utils/ # token counter, tool-message formatting
│ └── tests/ # pytest suite (agents/, base/, context/, connectors/, utils/)
Expand All @@ -19,7 +19,7 @@ atomic-agents/ # repo root (uv workspace)
├── atomic-forge/ # library of standalone tools (NOT a package)
│ ├── tools/<tool>/ # one folder per tool: tool/<tool>.py, tests/, pyproject.toml
│ └── guides/ # tool authoring guides (e.g. tool_structure.md)
├── atomic-examples/ # 15 runnable example apps (each its own project)
├── atomic-examples/ # 16 runnable example apps (each its own project)
├── docs/ # Sphinx + MyST documentation (api/, guides/, examples/)
├── guides/ # DEV_GUIDE.md and contributor guides
├── scripts/ # sync_version.py, generate_llms_files.py
Expand Down Expand Up @@ -49,9 +49,9 @@ and copies a selected tool into the user's project.
`pyproject.toml`/`requirements.txt`. Tools are copied into user projects, not pip-installed.

### `atomic-examples/`
15 standalone example apps (`quickstart`, `rag-chatbot`, `deep-research`, `web-search-agent`,
`mcp-agent`, `fastapi-memory`, `youtube-summarizer`, …), each with its own `pyproject.toml`. These
are excluded from the workspace build.
16 standalone example apps (`quickstart`, `rag-chatbot`, `deep-research`, `web-search-agent`,
`mcp-agent`, `fastapi-memory`, `persistent-memory`, `youtube-summarizer`, …), each with its own
`pyproject.toml`. These are excluded from the workspace build.

### `docs/` and `guides/`
`docs/` is a Sphinx + MyST site (`api/` reference, `guides/`, `examples/`, `conf.py`), deployed to
Expand Down
3 changes: 2 additions & 1 deletion .claude/.codebase-info/entry-points.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Entry Points

*Last Updated: 2026-06-13*
*Last Updated: 2026-07-05*

## 1. Library API (the primary entry point)

Expand Down Expand Up @@ -45,6 +45,7 @@ MCP server tools/resources/prompts become agent components via `atomic_agents.co
| `mcp-agent` | MCP client/server, multiple transports |
| `progressive-disclosure` | efficient MCP tool loading (3 servers, 24 tools) |
| `fastapi-memory` | multi-user / multi-session memory behind a FastAPI service |
| `persistent-memory` | custom `BaseChatHistory` backend: cross-session recall via stdlib `sqlite3` (no extra deps) |
| `hooks-example` | monitoring, error handling, retry via hooks |
| `basic-multimodal` / `nested-multimodal` | images / PDFs in (nested) schemas |
| `basic-pdf-analysis` | PDF analysis with a multimodal model |
Expand Down
11 changes: 7 additions & 4 deletions .claude/.codebase-info/modules.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Key Modules

*Last Updated: 2026-06-13*
*Last Updated: 2026-07-05*

## Core framework — `atomic-agents/atomic_agents/`

Expand All @@ -24,8 +24,11 @@
- **Location:** `atomic-agents/atomic_agents/context/`
- **Purpose:** System-prompt assembly and conversation memory.
- **Key files:** `system_prompt_generator.py` (`SystemPromptGenerator`, `BaseDynamicContextProvider`),
`chat_history.py` (`ChatHistory`, `Message` — multimodal Image/Audio/PDF, turn grouping,
`dump()`/`load()` serialization).
`base_chat_history.py` (`BaseChatHistory` — interface-only ABC declaring the memory contract
`AtomicAgent` depends on; the pluggable seam for custom/persistent backends),
`chat_history.py` (`ChatHistory`, `Message` — the built-in in-memory implementation of
`BaseChatHistory`: multimodal Image/Audio/PDF, turn grouping, `dump()`/`load()` serialization).
- **Note:** `AgentConfig.history` is typed to `BaseChatHistory`, so any conforming backend drops in.

### connectors/mcp
- **Location:** `atomic-agents/atomic_agents/connectors/mcp/`
Expand Down Expand Up @@ -58,4 +61,4 @@

### atomic-examples
- **Location:** `atomic-examples/`
- **Purpose:** 15 runnable reference apps. Catalog in `entry-points.md`.
- **Purpose:** 16 runnable reference apps. Catalog in `entry-points.md`.
14 changes: 10 additions & 4 deletions .claude/.codebase-info/patterns.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Patterns & Conventions

*Last Updated: 2026-06-13*
*Last Updated: 2026-07-05*

## Atomicity
Build with small, single-purpose, composable parts ("LEGO blocks"): each agent, tool, and context
Expand All @@ -25,9 +25,15 @@ the next's input. See `atomic-examples/deep-research` and `orchestration-agent`.
schema(s) → config → tool class + logic → example usage.

## Memory
- `ChatHistory` stores typed `Message`s grouped into turns (a user+assistant pair shares a `turn_id`),
is multimodal-aware (Image/Audio/PDF), and supports `dump()`/`load()`. `AtomicAgent` trims the
oldest whole turns to honor `max_context_tokens`.
- `BaseChatHistory` (`context/base_chat_history.py`) is an interface-only ABC declaring the memory
contract `AtomicAgent` relies on (`add_message`, `get_history`, turn handling, `dump()`/`load()`,
`copy()`, plus the `history`/`current_turn_id` attributes). It is the documented, dependency-free
seam for plugging in custom/persistent backends; `AgentConfig.history` is typed to it.
- `ChatHistory` is the built-in implementation: stores typed `Message`s grouped into turns (a
user+assistant pair shares a `turn_id`), is multimodal-aware (Image/Audio/PDF), and supports
`dump()`/`load()`. `AtomicAgent` trims the oldest whole turns to honor `max_context_tokens`.
- Custom backend pattern: subclass `ChatHistory` and override `add_message`/`load` to persist (see
the `persistent-memory` example and the "Writing a Custom Memory Backend" guide section).

## Error handling & retries
- Lean on Instructor's validation/retry of structured outputs, plus hook events (`parse:error`, …)
Expand Down
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ exclude_lines =
@abc.abstractmethod
if __name__ == .__main__.:
if __name__ == '__main__':
if TYPE_CHECKING:
if typing.TYPE_CHECKING:
pragma: no cover
# pragma: no cover

Expand Down
7 changes: 4 additions & 3 deletions atomic-agents/atomic_agents/agents/atomic_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Optional, Type, Generator, AsyncGenerator, get_args, get_origin, Dict, List, Callable, Any
import logging
from atomic_agents.context.chat_history import ChatHistory
from atomic_agents.context.base_chat_history import BaseChatHistory
from atomic_agents.context.system_prompt_generator import (
BaseDynamicContextProvider,
SystemPromptGenerator,
Expand Down Expand Up @@ -66,7 +67,7 @@ class BasicChatOutputSchema(BaseIOSchema):
class AgentConfig(BaseModel):
client: instructor.core.client.Instructor = Field(..., description="Client for interacting with the language model.")
model: str = Field(default="gpt-5-mini", description="The model to use for generating responses.")
history: Optional[ChatHistory] = Field(default=None, description="History component for storing chat history.")
history: Optional[BaseChatHistory] = Field(default=None, description="History component for storing chat history.")
system_prompt_generator: Optional[BaseSystemPromptGenerator] = Field(
default=None,
description=(
Expand Down Expand Up @@ -117,7 +118,7 @@ class AtomicAgent[InputSchema: BaseIOSchema, OutputSchema: BaseIOSchema]:
Attributes:
client: Client for interacting with the language model.
model (str): The model to use for generating responses.
history (ChatHistory): History component for storing chat history.
history (BaseChatHistory): History component for storing chat history.
system_prompt_generator (BaseSystemPromptGenerator): Component for generating system prompts.
system_role (Optional[str]): The role of the system in the conversation. None means no system prompt.
assistant_role (str): The role of the assistant in the conversation. Use 'model' for Gemini, 'assistant' for OpenAI/Anthropic.
Expand Down Expand Up @@ -189,7 +190,7 @@ def __init__(self, config: AgentConfig):
"""
self.client = config.client
self.model = config.model
self.history = config.history or ChatHistory()
self.history = config.history if config.history is not None else ChatHistory()
self.system_prompt_generator = config.system_prompt_generator or SystemPromptGenerator()
self.system_role = config.system_role
self.assistant_role = config.assistant_role
Expand Down
2 changes: 2 additions & 0 deletions atomic-agents/atomic_agents/context/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .base_chat_history import BaseChatHistory
from .chat_history import Message, ChatHistory
from .system_prompt_generator import (
BaseDynamicContextProvider,
Expand All @@ -7,6 +8,7 @@

__all__ = [
"Message",
"BaseChatHistory",
"ChatHistory",
"SystemPromptGenerator",
"BaseDynamicContextProvider",
Expand Down
152 changes: 152 additions & 0 deletions atomic-agents/atomic_agents/context/base_chat_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Dict, List, Optional

from atomic_agents.base.base_io_schema import BaseIOSchema

if TYPE_CHECKING:
from atomic_agents.context.chat_history import Message


class BaseChatHistory(ABC):
"""
Declares the public contract of a chat history.

This is the full method surface of the built-in ``ChatHistory``, of which ``AtomicAgent``
itself uses a subset (``add_message``, ``get_history``, ``initialize_turn``,
``delete_turn_id``, ``copy``, plus direct reads of the ``history`` / ``current_turn_id``
attributes). The remaining methods (``dump`` / ``load`` / ``get_message_count`` /
``get_current_turn_id``) round out the standard history surface that tooling and callers
rely on.

This is an interface-only abstract base class: it defines no state and no behavior,
only the methods a chat history implementation must provide. ``ChatHistory`` is the
framework's built-in, in-memory implementation. Implement this interface directly (or
subclass ``ChatHistory``) to plug in a custom persistent or backend-specific memory store.

Every implementation must maintain the following two attributes, since callers
(including ``AtomicAgent._trim_context``) read them directly:

Attributes:
history (List[Message]): The in-memory working list of messages that make up the
chat history. ``AtomicAgent._trim_context`` reads this list directly (via
``msg.turn_id``) to decide which turns to trim when a context-length limit
is exceeded.
current_turn_id (Optional[str]): The ID of the current turn, or ``None`` if no
turn has been started yet. Set by ``initialize_turn()``.
"""

history: List["Message"]
current_turn_id: Optional[str]

@abstractmethod
def initialize_turn(self) -> None:
"""
Starts a new turn, generating and storing a new turn ID.

Implementations must set ``self.current_turn_id`` to a new, unique value each
time this is called.
"""
raise NotImplementedError

@abstractmethod
def add_message(self, role: str, content: BaseIOSchema) -> None:
"""
Adds a message to the chat history, tagging it with the current turn ID.

If no turn has been started yet (``current_turn_id`` is ``None``), implementations
should start one (e.g. by calling ``initialize_turn()``) before recording the message.

Args:
role (str): The role of the message sender (e.g. 'user', 'system', 'assistant').
content (BaseIOSchema): The content of the message.
"""
raise NotImplementedError

@abstractmethod
def get_history(self) -> List[Dict]:
"""
Retrieves the chat history in the wire format expected by the LLM client.

Returns:
List[Dict]: The list of messages as dictionaries with 'role' and 'content' keys,
suitable for passing to a chat completion call.
"""
raise NotImplementedError

@abstractmethod
def get_current_turn_id(self) -> Optional[str]:
"""
Returns the current turn ID.

Returns:
Optional[str]: The current turn ID, or ``None`` if no turn has been started.
"""
raise NotImplementedError

@abstractmethod
def delete_turn_id(self, turn_id: str):
"""
Deletes all messages belonging to the given turn ID.

Args:
turn_id (str): The turn ID whose messages should be removed.

Raises:
ValueError: If the specified turn ID is not found in the history.
"""
raise NotImplementedError

@abstractmethod
def get_message_count(self) -> int:
"""
Returns the number of messages currently in the chat history.

Returns:
int: The number of messages.
"""
raise NotImplementedError

@abstractmethod
def dump(self) -> str:
"""
Serializes the entire chat history to a JSON string.

The returned string must be a valid argument to ``load()``, such that
``instance.load(instance.dump())`` round-trips the full history state.

Returns:
str: A JSON string representation of the chat history.
"""
raise NotImplementedError

@abstractmethod
def load(self, serialized_data: str) -> None:
"""
Deserializes a JSON string produced by ``dump()`` and loads it into this instance,
replacing any existing history state.

Args:
serialized_data (str): A JSON string representation of the chat history,
as produced by ``dump()``.

Raises:
ValueError: If the serialized data is invalid or cannot be deserialized.
"""
raise NotImplementedError

@abstractmethod
def copy(self) -> "BaseChatHistory":
"""
Creates a copy of the chat history, independent of the original.

``AtomicAgent`` calls ``copy()`` to snapshot ``initial_history`` at construction and
to restore it in ``reset_history()``. The built-in ``ChatHistory.copy()`` returns a
plain ``ChatHistory``; a subclass that carries extra state (a database handle, a
``session_id``, a store reference) MUST override ``copy()`` to return its own type,
otherwise ``reset_history()`` silently replaces the backend with a plain in-memory
history and later writes stop reaching the store.

Returns:
BaseChatHistory: A copy of the chat history.
"""
raise NotImplementedError
3 changes: 2 additions & 1 deletion atomic-agents/atomic_agents/context/chat_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pydantic import BaseModel, Field

from atomic_agents.base.base_io_schema import BaseIOSchema
from atomic_agents.context.base_chat_history import BaseChatHistory


INSTRUCTOR_MULTIMODAL_TYPES = (Image, Audio, PDF)
Expand All @@ -28,7 +29,7 @@ class Message(BaseModel):
turn_id: Optional[str] = None


class ChatHistory:
class ChatHistory(BaseChatHistory):
"""
Manages the chat history for an AI agent.

Expand Down
Loading
Loading