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
25 changes: 25 additions & 0 deletions docs/did_public/did_a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1",
"https://w3id.org/security/suites/secp256k1-2019/v1"
],
"id": "did:wba:didhost.cc:chata",
"verificationMethod": [
{
"id": "did:wba:didhost.cc:chata#key-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:wba:didhost.cc:chata",
"publicKeyJwk": {
"kty": "EC",
"crv": "secp256k1",
"x": "3QhHtIA9OrPys7Ih-C7ypn_wo0hURSJsftiO0InGX3Y",
"y": "AM3LVa-IHswIJdMq6-qXYE649545VNTAvzan6DM_jCM",
"kid": "chata-key-2026-01"
}
}
],
"authentication": [
"did:wba:didhost.cc:chata#key-1"
]
}
25 changes: 25 additions & 0 deletions docs/did_public/did_b.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1",
"https://w3id.org/security/suites/secp256k1-2019/v1"
],
"id": "did:wba:didhost.cc:chatb",
"verificationMethod": [
{
"id": "did:wba:didhost.cc:chatb#key-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:wba:didhost.cc:chatb",
"publicKeyJwk": {
"kty": "EC",
"crv": "secp256k1",
"x": "3QhHtIA9OrPys7Ih-C7ypn_wo0hURSJsftiO0InGX3Y",
"y": "AM3LVa-IHswIJdMq6-qXYE649545VNTAvzan6DM_jCM",
"kid": "chatb-main-key-2026"
}
}
],
"authentication": [
"did:wba:didhost.cc:chatb#key-1"
]
}
6 changes: 6 additions & 0 deletions docs/did_public/private_a.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgMd0Zh3TA+KzOh+aL5g76
sz34ULdhjGkv4NEYnoGe7nehRANCAATdCEe0gD06s/KzsiH4LvKmf/CjSFRFImx+
2I7QicZfdgDNy1WviB7MCCXTKuvql2BOuPeeOVTUwL82p+gzP4wj
-----END PRIVATE KEY-----
-----END PRIVATE KEY-----
5 changes: 5 additions & 0 deletions docs/did_public/private_b.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgMd0Zh3TA+KzOh+aL5g76
sz34ULdhjGkv4NEYnoGe7nehRANCAATdCEe0gD06s/KzsiH4LvKmf/CjSFRFImx+
2I7QicZfdgDNy1WviB7MCCXTKuvql2BOuPeeOVTUwL82p+gzP4wj
-----END PRIVATE KEY-----
60 changes: 59 additions & 1 deletion examples/python/openanp_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ result = await agent.hello(name="World") # "Hello, World!"
| `minimal_client.py` | Minimal client | ⭐ |
| `advanced_server.py` | Full features (Context, Session, Information) | ⭐⭐⭐ |
| `advanced_client.py` | Full client (discovery, error handling, LLM integration) | ⭐⭐⭐ |

| `chat_a.py` | Chat Agent A (discovery, receive message, LLM integration) | ⭐⭐⭐ |
| `chat_b.py` | Chat Agent B (discovery, receive message, LLM integration) | ⭐⭐⭐ |
---

## 🏃 Running Examples
Expand Down Expand Up @@ -199,10 +200,67 @@ curl http://localhost:8000/agent/ad.json | jq
```bash
curl http://localhost:8000/agent/interface.json | jq
```
## 💬 Chat Example
### Run Chat Example

```bash
# Terminal 1: Start Chat Agent A
uv run python examples/python/openanp_examples/chat_a.py

# Terminal 2: Start Chat Agent B (in another terminal)
uv run python examples/python/openanp_examples/chat_b.py
```

### Chat Agent Architecture

**Core Agent Structure (chat_a.py & chat_b.py)**

```python
@anp_agent(AgentConfig(
name="ChatA",
did="did:wba:example.com:chata",
prefix="/a",
))
class ChatAgentA:
@interface
async def notify_connected(self, agent: str) -> dict:
"""Called when peer agent connects"""
return {"ok": True, "agent": "ChatA", "connected": agent}

@interface
async def receive_message(self, message: str, remaining_turns: int) -> dict:
"""Receive message and reply using LLM"""
reply = self._llm_reply(message) # OpenAI or fallback
remaining_turns = max(0, remaining_turns - 1)
return {
"agent": "ChatA",
"reply": reply,
"remaining_turns": remaining_turns,
}

@interface
async def propose_chat(self, initiator_did: str, initiator_discover_ts: float,
session_id: str, turns: int = 4) -> dict:
"""Peer requests to initiate chat with tie-breaking"""
# Deterministic tie-break using DID when both discover simultaneously
if AGENT_A_DID < initiator_did:
return {"accepted": False, "reason": "tie_break"}
return {"accepted": True, "session_id": session_id}
```
### Generated Endpoints

| Endpoint | Description |
|----------|-------------|
| `GET /` | status |
| `GET /health` | health check |
| `POST /p2p/discover` | trigger discovery and cache the peer connection |
| `POST /p2p/send` | send a message to the peer (internally calls peer `receive_message`) |

---

## 📖 More Resources

- [ANP Protocol Specification](https://github.com/agent-network-protocol)
- [AgentConnect Documentation](../../../docs/)

---
Loading