Skip to content

Commit 35614f8

Browse files
feat: add 07_agents.py update readme/agents.md
1 parent d75c04a commit 35614f8

File tree

14 files changed

+666
-199
lines changed

14 files changed

+666
-199
lines changed

AGENTS.md

Lines changed: 81 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,27 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
1919
- 🤝 **Collaborative Tools** - Real-time editing/voting
2020
- ⚠️ **If users in different terminals/processes need to see each other's data = MULTI-USER**
2121

22+
**📁 MANDATORY: Study `07_agents.py` BEFORE building multi-user apps!**
23+
```bash
24+
# Read the reference implementation first:
25+
cat src/arkiv_starter/07_agents.py
26+
27+
# Run it to understand how it works:
28+
uv run python -m arkiv_starter.07_agents demo
29+
```
30+
This file contains the **canonical patterns** for multi-user apps:
31+
- `ArkivChain` class → Server pattern (runs shared blockchain node)
32+
- `Client` class → Client pattern (connects to shared node)
33+
- `run_demo()` → Shows how multiple clients interact on same chain
34+
2235
**Single-User = Can Use Simple Pattern:**
2336
- 📊 **Personal Storage** - One user's data
2437
- 🤖 **Automation/Bots** - Scripts run alone
2538
- 🧪 **Tests** - Use pytest fixtures
2639
- 🎯 **Single-Player Games** - No interaction between processes
2740

2841
**Critical Rule for AI Assistants:**
29-
When user says "chat", "social", "multiplayer", "collaborative" → **IGNORE** any request for "simple" or "single file" **MUST ask the user to confirm to use server + client pattern**. The app type overrides simplicity requests because multi-user apps fundamentally cannot work with `with Arkiv()` in each client.
42+
When user says "chat", "social", "multiplayer", "collaborative" → **STOP****Read `07_agents.py` first** → Use that as your template. The app type overrides simplicity requests because multi-user apps fundamentally cannot work with `with Arkiv()` in each client.
3043

3144
---
3245

@@ -48,9 +61,10 @@ When user says "chat", "social", "multiplayer", "collaborative" → **IGNORE** a
4861
-`NamedAccount.create(name)` → for local dev (not Account.create())
4962

5063
**Multi-user interactive apps (chat/social/gaming):**
51-
-**Server + Client pattern** → server.py runs ONE node, demo.py connects via HTTP
52-
- ✅ Clients use `HTTPProvider("http://127.0.0.1:8545")` to connect to shared server
53-
-**NEVER** `with Arkiv()` in multi-user demo.py (creates separate blockchains per terminal!)
64+
- 📁 **FIRST: Read `src/arkiv_starter/07_agents.py`** - This is your template!
65+
-**Server + Client pattern** → Copy `ArkivChain` for server, `Client` for clients
66+
- ✅ Clients use `ProviderBuilder().custom(url=rpc_url).build()` to connect to shared server
67+
-**NEVER** `with Arkiv()` in multi-user client code (creates separate blockchains!)
5468
-**NEVER** let "simple" override multi-user requirement (won't work at all!)
5569

5670
**Time conversion (methods, not imports):**
@@ -75,7 +89,7 @@ When user says "chat", "social", "multiplayer", "collaborative" → **IGNORE** a
7589
- ❌ DON'T call `configure_python_environment` - it's automatic
7690
- ✅ Just run: `uv run python -m app.demo` or `uv run pytest`
7791

78-
**Run examples:** `uv run python -m arkiv_starter.01_clients` (etc., 01-05)
92+
**Run examples:** `uv run python -m arkiv_starter.03_clients` (etc., 01-08)
7993

8094
---
8195

@@ -493,11 +507,11 @@ account = Account.create() # LocalAccount won't work with node.fund_account()
493507
```python
494508
# ✅ CORRECT - When you have an account object
495509
print(account.address) # Ethereum address
496-
print(account.private_key) # Private key for signing
510+
print(account.key) # Private key for signing (bytes)
497511
print(account.name) # Account name (NamedAccount only)
498512

499513
# ❌ WRONG
500-
print(account.key) # Use account.private_key instead
514+
print(account.private_key) # Use account.key instead
501515
```
502516

503517
### Accessing the Current Account from Client
@@ -564,11 +578,14 @@ client2 = Arkiv(provider, account=account2) # Wasteful
564578

565579
```bash
566580
# ✅ CORRECT - Run as modules
567-
uv run python -m arkiv_starter.01_clients
568-
uv run python -m arkiv_starter.02_entity_crud
569-
uv run python -m arkiv_starter.03_queries
570-
uv run python -m arkiv_starter.04_events
571-
uv run python -m arkiv_starter.05_web3_integration
581+
uv run python -m arkiv_starter.01_hello_world
582+
uv run python -m arkiv_starter.02_accounts
583+
uv run python -m arkiv_starter.03_clients
584+
uv run python -m arkiv_starter.04_entity_crud
585+
uv run python -m arkiv_starter.05_queries
586+
uv run python -m arkiv_starter.06_events
587+
uv run python -m arkiv_starter.07_agents
588+
uv run python -m arkiv_starter.08_web3_integration
572589

573590
# ✅ CORRECT - Run tests
574591
uv run pytest
@@ -898,7 +915,7 @@ from arkiv import to_seconds, to_blocks # ImportError!
898915
- `module_base.py`: All method signatures with detailed docstrings
899916
- Use `semantic_search("method_name implementation")` to find SDK code
900917
- SDK docstrings are the source of truth for parameters, return types, and behavior
901-
- **examples/**: 5 progressive tutorials (01→05)
918+
- **src/arkiv_starter/**: 8 progressive tutorials (01→08)
902919
- **tests/**: Working test patterns to learn from
903920

904921
---
@@ -1166,7 +1183,7 @@ client.arkiv.watch_entity_created(on_entity_created)
11661183

11671184
1. **Check SDK source code for method details** - Use `semantic_search()` to find implementation in `.venv/lib/python3.12/site-packages/arkiv/module_base.py`
11681185
2. **SDK docstrings are source of truth** - All methods have comprehensive docs with examples
1169-
3. **Use the examples as templates** - they demonstrate correct patterns (01→05)
1186+
3. **Use the examples as templates** - they demonstrate correct patterns (01→08)
11701187
4. **Run tests after changes** - `uv run pytest -n auto`
11711188
5. **Check entity.payload is not None** before decoding - it's optional
11721189
6. **Use `cast(BaseProvider, provider)` for type checking** if IDE shows errors
@@ -1420,12 +1437,29 @@ def test_retrieve_entities(arkiv_client):
14201437

14211438
### 🚨 CRITICAL: Multi-User Interactive Applications (Social, Gaming, etc.)
14221439

1423-
**⚠️ STOP AND READ THIS FIRST** if you're building:
1440+
**⚠️ STOP AND READ `07_agents.py` FIRST** if you're building:
14241441
- Any app where multiple independent users interact in real-time
14251442
- Social media/messaging apps
14261443
- Multiplayer games
14271444

1428-
**The Problem:**
1445+
**📁 MANDATORY: Use `src/arkiv_starter/07_agents.py` as your template!**
1446+
1447+
```bash
1448+
# Step 1: Read the reference implementation
1449+
cat src/arkiv_starter/07_agents.py
1450+
1451+
# Step 2: Run it to see how it works
1452+
uv run python -m arkiv_starter.07_agents demo
1453+
1454+
# Step 3: Copy patterns from 07_agents.py into your app
1455+
```
1456+
1457+
**Key classes to copy from `07_agents.py`:**
1458+
- `ArkivChain` → Your server (runs ONE shared blockchain node)
1459+
- `Client` → Your client (connects to shared node via RPC URL)
1460+
- `run_demo()` → Shows complete multi-client interaction
1461+
1462+
**The Problem (why you MUST use the 07_agents.py pattern):**
14291463
When you run a demo script like `python -m app.demo` in multiple terminals, each creates its **own separate blockchain**. Users can't see each other!
14301464

14311465
**WRONG - Separate Blockchains (Most Common Mistake):**
@@ -1462,129 +1496,44 @@ $ uv run python -m app.demo
14621496
- Tests work fine (shared fixtures), but real demo breaks
14631497
- This is THE #1 mistake for multi-user interactive apps
14641498

1465-
**CORRECT Solution: Server + Client Architecture**
1466-
1467-
For interactive multi-user apps, you need **TWO types of scripts**:
1499+
**CORRECT Solution: Server + Client Architecture (See `07_agents.py`)**
14681500

1469-
#### 1. **Server Script** - Runs the shared node (one instance only)
1501+
**📁 The canonical implementation is in `src/arkiv_starter/07_agents.py`.**
14701502

1471-
```python
1472-
# src/app/server.py
1473-
"""Arkiv app server - Run this ONCE to start the shared blockchain node."""
1503+
For interactive multi-user apps, you need **TWO types of scripts**:
14741504

1475-
import time
1476-
from arkiv import Arkiv
1505+
1. **Server Script** (`ArkivChain` class in `07_agents.py`) - Runs the shared node
1506+
2. **Client Script** (`Client` class in `07_agents.py`) - Connects to the shared node
14771507

1478-
def main():
1479-
print("🚀 Starting Arkiv App Server...")
1480-
print("📡 Server running on http://127.0.0.1:8545")
1481-
print("💡 Keep this running while users interact")
1482-
print("⏹️ Press Ctrl+C to stop\n")
1483-
1484-
with Arkiv() as client:
1485-
node = client.node
1486-
assert node is not None
1487-
1488-
print(f"✅ Server ready! RPC endpoint: {node.rpc_url}")
1489-
print(f"📊 Network ID: {client.eth.chain_id}\n")
1490-
1491-
try:
1492-
# Keep server running
1493-
while True:
1494-
time.sleep(1)
1495-
except KeyboardInterrupt:
1496-
print("\n\n🛑 Shutting down server...")
1497-
1498-
print("✅ Server stopped.\n")
1508+
```bash
1509+
# Study the reference implementation:
1510+
cat src/arkiv_starter/07_agents.py
14991511

1500-
if __name__ == "__main__":
1501-
main()
1512+
# Run it to see server + client in action:
1513+
uv run python -m arkiv_starter.07_agents demo
15021514
```
15031515

1504-
#### 2. **Client Demo Script** - Connects to the shared node
1516+
**Key patterns from `07_agents.py`:**
15051517

15061518
```python
1507-
# src/app/demo.py
1508-
"""Arkiv app client - Run multiple instances to interact together."""
1519+
# SERVER: Create ONE shared blockchain node
1520+
class ArkivChain:
1521+
def __init__(self):
1522+
self._client = Arkiv()
1523+
self._node = self._client.node
1524+
# Node exposes: self._node.rpc_url (default: http://127.0.0.1:8545)
15091525

1510-
import time
1511-
from typing import cast
1512-
from web3 import Web3
1513-
from web3.providers import HTTPProvider
1514-
from web3.providers.base import BaseProvider
1515-
from arkiv import Arkiv, NamedAccount
1516-
1517-
def main():
1518-
print("\n🎉 Welcome to Arkiv App! 🎉\n")
1519-
1520-
# Get username
1521-
username = input("Enter your username: ").strip()
1522-
if not username:
1523-
username = "Anonymous"
1524-
1525-
print(f"\n📡 Connecting to Arkiv server at http://127.0.0.1:8545...")
1526-
1527-
# Connect to shared server
1528-
provider = cast(BaseProvider, HTTPProvider("http://127.0.0.1:8545"))
1529-
1530-
# Create account for this user
1531-
account = NamedAccount.create(username.lower().replace(" ", "-"))
1532-
1533-
# Initialize client connected to shared blockchain
1534-
client = Arkiv(provider=provider, account=account)
1535-
1536-
# Fund account (server must be running!)
1537-
try:
1538-
# Check if we can connect
1539-
client.eth.chain_id
1540-
print("✅ Connected to server!")
1541-
except Exception as e:
1542-
print(f"❌ Cannot connect to server. Is it running?")
1543-
print(f" Start server with: uv run python -m app.server")
1544-
return
1545-
1546-
print(f"\n✅ Connected as {username}...")
1547-
print("⌨️ Interact with the app, '/quit' to exit\n")
1548-
1549-
# Create your app client
1550-
from app.client import AppClient
1551-
app = AppClient(client, username=username)
1552-
1553-
# Your app-specific initialization
1554-
# Example: Load recent data, set up event watchers, etc.
1555-
app.initialize()
1556-
time.sleep(0.2)
1557-
1558-
# Interactive loop
1559-
try:
1560-
while True:
1561-
user_input = input("> ").strip()
1562-
1563-
if not user_input:
1564-
continue
1565-
1566-
if user_input.lower() == "/quit":
1567-
print("\n👋 Goodbye!")
1568-
break
1569-
1570-
# Your app-specific command handling
1571-
try:
1572-
app.handle_command(user_input)
1573-
except Exception as e:
1574-
print(f"❌ Error: {e}")
1575-
1576-
except KeyboardInterrupt:
1577-
print("\n\n👋 Interrupted. Goodbye!")
1578-
1579-
finally:
1580-
app.cleanup()
1581-
1582-
print("✅ Disconnected.\n")
1583-
1584-
if __name__ == "__main__":
1585-
main()
1526+
# CLIENT: Connect to shared node via RPC URL
1527+
class Client:
1528+
def __init__(self, name: str, rpc_url: str = DEFAULT_RPC_URL):
1529+
provider = cast(BaseProvider, ProviderBuilder().custom(url=rpc_url).build())
1530+
self._account = NamedAccount.create(name)
1531+
self._client = Arkiv(provider=provider, account=self._account)
1532+
# Client shares blockchain with server!
15861533
```
15871534

1535+
**Copy these patterns from `07_agents.py` into your app!**
1536+
15881537
#### Usage Instructions (Add to README)
15891538

15901539
```markdown
@@ -1674,16 +1623,17 @@ Create a [application name] using Arkiv SDK with the following:
16741623
2. Demo Scripts (CRITICAL - Choose based on app type):
16751624
16761625
**If Multi-User Interactive App (chat, social, multiplayer game):**
1677-
- src/[app_name]/server.py - Runs shared node (one instance)
1678-
- src/[app_name]/demo.py - Client that connects to server (multiple instances)
1626+
- 📁 FIRST: Read src/arkiv_starter/07_agents.py as your template
1627+
- Copy ArkivChain pattern for server.py
1628+
- Copy Client pattern for demo.py
16791629
- README must explain: "Start server first, then run demo in multiple terminals"
16801630
16811631
**If Single-User App (personal storage, automation, single-player):**
16821632
- src/[app_name]/demo.py - Simple `with Arkiv() as client` pattern
16831633
- No server needed
16841634
16851635
3. Implementation Requirements:
1686-
- For multi-user: Create ONE shared node (server), clients connect via HTTP
1636+
- For multi-user: Copy patterns from 07_agents.py (ArkivChain + Client classes)
16871637
- For single-user: Use context manager pattern: with Arkiv() as client
16881638
- Always call client.node.stop() or use context manager for cleanup
16891639
- Add basic test that verifies core functionality
@@ -1743,5 +1693,5 @@ if __name__ == "__main__":
17431693

17441694
---
17451695

1746-
*Last updated: 2025-11-25*
1696+
*Last updated: 2025-06-02*
17471697
*This file works with all AI coding tools that support the AGENTS.md standard.*

0 commit comments

Comments
 (0)