Skip to content

Commit e233e7e

Browse files
authored
Merge pull request #1 from toondb/release/0.3.3
Core fixes: SDKs now expose real monotonic commit timestamps, databas…
2 parents 2f0e8e7 + 2e4764f commit e233e7e

9 files changed

Lines changed: 3635 additions & 55 deletions

File tree

README.md

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,155 @@ pip install toondb-client
4343
4444
> ℹ️ **About the Binaries**: This Python SDK packages pre-compiled binaries from the [main ToonDB repository](https://github.com/toondb/toondb). Each wheel contains platform-specific executables (`toondb-bulk`, `toondb-server`, `toondb-grpc-server`) and native FFI libraries. See [RELEASE.md](RELEASE.md) for details on the release process.
4545
46-
## What's New in Latest Release
46+
## What's New in v0.3.3
47+
48+
### 🕸️ Graph Overlay for Agent Memory
49+
Build lightweight graph structures on top of ToonDB's KV storage for agent memory:
50+
51+
```python
52+
from toondb import Database, GraphOverlay
53+
54+
db = Database.open("./agent_db")
55+
graph = GraphOverlay(db, namespace="agent_memory")
56+
57+
# Add nodes (entities, concepts, events)
58+
graph.add_node("user_alice", "person", {"name": "Alice", "role": "developer"})
59+
graph.add_node("conv_123", "conversation", {"topic": "ToonDB features"})
60+
graph.add_node("action_456", "action", {"type": "code_commit", "status": "success"})
61+
62+
# Add edges (relationships, causality, references)
63+
graph.add_edge("user_alice", "started", "conv_123", {"timestamp": "2026-01-05"})
64+
graph.add_edge("conv_123", "triggered", "action_456", {"reason": "user request"})
65+
66+
# Retrieve nodes and edges
67+
node = graph.get_node("user_alice")
68+
edges = graph.get_edges("user_alice", edge_type="started")
69+
70+
# Graph traversal
71+
visited = graph.bfs_traversal("user_alice", max_depth=3) # BFS from Alice
72+
path = graph.shortest_path("user_alice", "action_456") # Find connection
73+
74+
# Get neighbors
75+
neighbors = graph.get_neighbors("conv_123", direction="both")
76+
77+
# Extract subgraph
78+
subgraph = graph.get_subgraph(["user_alice", "conv_123", "action_456"])
79+
```
80+
81+
**Use Cases:**
82+
- Agent conversation history with causal chains
83+
- Entity relationship tracking across sessions
84+
- Action dependency graphs for planning
85+
- Knowledge graph construction
86+
87+
### 🛡️ Policy & Safety Hooks
88+
Enforce safety policies on agent operations with pre/post triggers:
89+
90+
```python
91+
from toondb import Database, PolicyEngine, PolicyAction
92+
93+
db = Database.open("./agent_data")
94+
policy = PolicyEngine(db)
95+
96+
# Block writes to system keys from agents
97+
@policy.before_write("system/*")
98+
def block_system_writes(key, value, context):
99+
if context.get("agent_id"):
100+
return PolicyAction.DENY
101+
return PolicyAction.ALLOW
102+
103+
# Redact sensitive data on read
104+
@policy.after_read("users/*/email")
105+
def redact_emails(key, value, context):
106+
if context.get("redact_pii"):
107+
return b"[REDACTED]"
108+
return value
109+
110+
# Rate limit writes per agent
111+
policy.add_rate_limit("write", max_per_minute=100, scope="agent_id")
112+
113+
# Enable audit logging
114+
policy.enable_audit()
115+
116+
# Use policy-wrapped operations
117+
policy.put(b"users/alice", b"data", context={"agent_id": "agent_001"})
118+
```
119+
120+
### 🔀 Multi-Agent Tool Routing
121+
Route tool calls to specialized agents with automatic failover:
122+
123+
```python
124+
from toondb import Database, ToolDispatcher, ToolCategory, RoutingStrategy
125+
126+
db = Database.open("./agent_data")
127+
dispatcher = ToolDispatcher(db)
128+
129+
# Register agents with capabilities
130+
dispatcher.register_local_agent(
131+
"code_agent",
132+
capabilities=[ToolCategory.CODE, ToolCategory.GIT],
133+
handler=lambda tool, args: {"result": f"Processed {tool}"},
134+
)
135+
136+
dispatcher.register_remote_agent(
137+
"search_agent",
138+
capabilities=[ToolCategory.SEARCH],
139+
endpoint="http://localhost:8001/invoke",
140+
)
141+
142+
# Register tools
143+
dispatcher.register_tool(
144+
name="search_code",
145+
description="Search codebase",
146+
category=ToolCategory.CODE,
147+
)
148+
149+
# Invoke with automatic routing (priority, round-robin, fastest, etc.)
150+
result = dispatcher.invoke("search_code", {"query": "auth"}, session_id="sess_001")
151+
print(f"Routed to: {result.agent_id}, Success: {result.success}")
152+
```
153+
154+
### 🕸️ Graph Overlay
155+
Lightweight graph layer for agent memory relationships:
156+
157+
```python
158+
from toondb import Database, GraphOverlay, TraversalOrder
159+
160+
db = Database.open("./agent_data")
161+
graph = GraphOverlay(db)
162+
163+
# Add nodes (entities, concepts, events)
164+
graph.add_node("user:alice", node_type="user", properties={"role": "admin"})
165+
graph.add_node("project:toondb", node_type="project", properties={"status": "active"})
166+
167+
# Add relationships
168+
graph.add_edge("user:alice", "project:toondb", edge_type="owns", properties={"since": "2024"})
169+
170+
# Traverse graph (BFS/DFS)
171+
related = graph.bfs("user:alice", max_depth=2, edge_filter=lambda e: e.edge_type == "owns")
172+
173+
# Find shortest path
174+
path = graph.shortest_path("user:alice", "project:toondb")
175+
```
176+
177+
### 🔗 Unified Connection API
178+
Single entry point with auto-detection:
179+
180+
```python
181+
import toondb
182+
183+
# Auto-detects embedded mode from path
184+
db = toondb.connect("./my_database")
185+
186+
# Auto-detects IPC mode from socket
187+
db = toondb.connect("/tmp/toondb.sock")
188+
189+
# Auto-detects gRPC mode from host:port
190+
db = toondb.connect("localhost:50051")
191+
192+
# Explicit mode
193+
db = toondb.connect("./data", mode="embedded", config={"sync_mode": "full"})
194+
```
47195
48196
### 🎯 Namespace Isolation
49197
Logical database namespaces for true multi-tenancy without key prefixing:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "toondb-client"
7-
version = "0.3.2"
7+
version = "0.3.3"
88
description = "ToonDB is an AI-native database with token-optimized output, O(|path|) lookups, built-in vector search, and durable transactions."
99
readme = "README.md"
1010
license = {text = "Apache-2.0"}

src/toondb/__init__.py

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,44 @@
6868
estimate_tokens,
6969
split_by_tokens,
7070
)
71+
from .graph import (
72+
# Graph Overlay (Task 10)
73+
GraphOverlay,
74+
GraphNode,
75+
GraphEdge,
76+
TraversalOrder,
77+
)
78+
from .policy import (
79+
# Policy & Safety Hooks (Task 11)
80+
PolicyEngine,
81+
PolicyAction,
82+
PolicyTrigger,
83+
PolicyResult,
84+
PolicyContext,
85+
PolicyHandler,
86+
PatternPolicy,
87+
RateLimiter,
88+
PolicyViolation,
89+
# Built-in policy helpers
90+
deny_all,
91+
allow_all,
92+
require_agent_id,
93+
redact_value,
94+
log_and_allow,
95+
)
96+
from .routing import (
97+
# Tool Routing (Task 12)
98+
ToolRouter,
99+
AgentRegistry,
100+
ToolDispatcher,
101+
Tool,
102+
Agent,
103+
ToolCategory,
104+
RoutingStrategy,
105+
AgentStatus,
106+
RouteResult,
107+
RoutingContext,
108+
)
71109

72110
# Vector search (optional - requires libtoondb_index)
73111
try:
@@ -104,8 +142,144 @@
104142
track_batch_insert = None
105143
is_analytics_disabled = lambda: True
106144

107-
__version__ = "0.3.1"
145+
__version__ = "0.3.3"
146+
147+
148+
# =============================================================================
149+
# Unified Connection API (Task 9: Standardize Deployment Modes)
150+
# =============================================================================
151+
152+
from enum import Enum
153+
from typing import Optional, Union
154+
155+
156+
class ConnectionMode(Enum):
157+
"""ToonDB connection mode."""
158+
EMBEDDED = "embedded" # Direct FFI to Rust library
159+
IPC = "ipc" # Unix socket to local server
160+
GRPC = "grpc" # gRPC to remote server
161+
162+
163+
def connect(
164+
path_or_url: str,
165+
mode: Optional[Union[str, ConnectionMode]] = None,
166+
config: Optional[dict] = None,
167+
) -> Union[Database, IpcClient]:
168+
"""
169+
Connect to ToonDB with automatic mode detection.
170+
171+
This is the unified entry point for all ToonDB connection modes.
172+
If mode is not specified, it auto-detects based on the path/URL:
173+
174+
- Embedded: File paths (./data, /tmp/db, ~/toondb)
175+
- IPC: Unix socket paths (/tmp/toondb.sock, unix://...)
176+
- gRPC: URLs with grpc:// scheme or host:port format
177+
178+
Args:
179+
path_or_url: Database path, socket path, or gRPC URL
180+
mode: Optional explicit mode ('embedded', 'ipc', 'grpc' or ConnectionMode enum)
181+
config: Optional configuration dict (passed to underlying client)
182+
183+
Returns:
184+
Database, IpcClient, or GrpcClient depending on mode
185+
186+
Examples:
187+
# Embedded mode (auto-detected from file path)
188+
db = toondb.connect("./my_database")
189+
db.put(b"key", b"value")
190+
191+
# IPC mode (auto-detected from .sock extension)
192+
db = toondb.connect("/tmp/toondb.sock")
193+
194+
# gRPC mode (auto-detected from host:port)
195+
db = toondb.connect("localhost:50051")
196+
197+
# Explicit mode
198+
db = toondb.connect("./data", mode="embedded", config={
199+
"sync_mode": "full",
200+
"index_policy": "scan_optimized",
201+
})
202+
203+
# Using enum
204+
db = toondb.connect("localhost:50051", mode=toondb.ConnectionMode.GRPC)
205+
"""
206+
# Normalize mode to enum
207+
if mode is None:
208+
detected_mode = _detect_mode(path_or_url)
209+
elif isinstance(mode, str):
210+
try:
211+
detected_mode = ConnectionMode(mode.lower())
212+
except ValueError:
213+
raise ValueError(
214+
f"Invalid mode '{mode}'. Valid modes: embedded, ipc, grpc"
215+
)
216+
else:
217+
detected_mode = mode
218+
219+
# Create appropriate client
220+
if detected_mode == ConnectionMode.EMBEDDED:
221+
return Database.open(path_or_url, config=config)
222+
223+
elif detected_mode == ConnectionMode.IPC:
224+
socket_path = path_or_url
225+
if socket_path.startswith("unix://"):
226+
socket_path = socket_path[7:] # Strip unix:// prefix
227+
return IpcClient(socket_path)
228+
229+
elif detected_mode == ConnectionMode.GRPC:
230+
try:
231+
from .grpc_client import GrpcClient
232+
url = path_or_url
233+
if url.startswith("grpc://"):
234+
url = url[7:] # Strip grpc:// prefix
235+
return GrpcClient(url)
236+
except ImportError:
237+
raise ImportError(
238+
"gRPC mode requires grpc dependencies. "
239+
"Install with: pip install toondb[grpc]"
240+
)
241+
242+
else:
243+
raise ValueError(f"Unknown connection mode: {detected_mode}")
244+
245+
246+
def _detect_mode(path_or_url: str) -> ConnectionMode:
247+
"""Auto-detect connection mode from path/URL format."""
248+
import os
249+
250+
# Explicit scheme detection
251+
if path_or_url.startswith("grpc://"):
252+
return ConnectionMode.GRPC
253+
if path_or_url.startswith("unix://"):
254+
return ConnectionMode.IPC
255+
256+
# Socket file detection
257+
if path_or_url.endswith(".sock"):
258+
return ConnectionMode.IPC
259+
if "/tmp/" in path_or_url and "sock" in path_or_url.lower():
260+
return ConnectionMode.IPC
261+
262+
# Host:port detection (gRPC)
263+
if ":" in path_or_url:
264+
parts = path_or_url.rsplit(":", 1)
265+
if len(parts) == 2:
266+
try:
267+
port = int(parts[1])
268+
if 1 <= port <= 65535:
269+
# Looks like host:port - probably gRPC
270+
return ConnectionMode.GRPC
271+
except ValueError:
272+
pass
273+
274+
# Default to embedded for file paths
275+
return ConnectionMode.EMBEDDED
276+
277+
108278
__all__ = [
279+
# Unified API (Task 9)
280+
"connect",
281+
"ConnectionMode",
282+
109283
# Core
110284
"Database",
111285
"Transaction",
@@ -129,6 +303,40 @@
129303
"SearchResult",
130304
"SearchResults",
131305

306+
# Graph Overlay (Task 10)
307+
"GraphOverlay",
308+
"GraphNode",
309+
"GraphEdge",
310+
"TraversalOrder",
311+
312+
# Policy & Safety Hooks (Task 11)
313+
"PolicyEngine",
314+
"PolicyAction",
315+
"PolicyTrigger",
316+
"PolicyResult",
317+
"PolicyContext",
318+
"PolicyHandler",
319+
"PatternPolicy",
320+
"RateLimiter",
321+
"PolicyViolation",
322+
"deny_all",
323+
"allow_all",
324+
"require_agent_id",
325+
"redact_value",
326+
"log_and_allow",
327+
328+
# Tool Routing (Task 12)
329+
"ToolRouter",
330+
"AgentRegistry",
331+
"ToolDispatcher",
332+
"Tool",
333+
"Agent",
334+
"ToolCategory",
335+
"RoutingStrategy",
336+
"AgentStatus",
337+
"RouteResult",
338+
"RoutingContext",
339+
132340
# ContextQuery (Task 12)
133341
"ContextQuery",
134342
"ContextResult",

0 commit comments

Comments
 (0)