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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ if __name__ == "__main__":
main()
```

### Registering with AgentFacts

Pass an `agent_facts` dict to publish skills and capabilities at registration.
See [agentfacts](https://github.com/projnanda/agentfacts) for the schema.

```python
agent_facts = {
"agent_name": "pirate-bot",
"label": "Pirate Translator",
"description": "Translates messages into pirate English",
"version": "1.0.0",
"skills": [
{"id": "translate.pirate", "description": "Translate to pirate English"}
],
}

nanda = NANDA(my_improvement, agent_facts=agent_facts)
nanda.start_server_api(anthropic_key, domain)
```

If running `agent_bridge.py` directly, set `AGENT_FACTS_PATH` to a JSON file.

### Deploy a LangChain Agent

```python
Expand Down
18 changes: 16 additions & 2 deletions nanda_adapter/core/agent_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def get_registry_url():
print(f"Using default registry URL: {default_url}")
return default_url

def register_with_registry(agent_id, agent_url, api_url):
def register_with_registry(agent_id, agent_url, api_url, agent_facts=None):
"""Register this agent with the registry"""
registry_url = get_registry_url()
try:
Expand All @@ -89,6 +89,8 @@ def register_with_registry(agent_id, agent_url, api_url):
"agent_url": agent_url,
"api_url": api_url
}
if agent_facts:
data["agent_facts"] = agent_facts
print(f"Registering agent {agent_id} with URL {agent_url} at registry {registry_url}...")
response = requests.post(f"{registry_url}/register", json=data)
if response.status_code == 200:
Expand Down Expand Up @@ -878,13 +880,25 @@ def handle_message(self, msg: Message) -> Message:
conversation_id = conversation_id
)

def _load_agent_facts_from_env():
"""Load agent_facts JSON from AGENT_FACTS_PATH if set"""
facts_path = os.getenv("AGENT_FACTS_PATH")
if not facts_path:
return None
try:
with open(facts_path, "r") as f:
return json.load(f)
except Exception as e:
print(f"Could not read agent facts from {facts_path}: {e}")
return None

if __name__ == "__main__":
# Register with the registry if PUBLIC_URL is set
public_url = os.getenv("PUBLIC_URL")
api_url = os.getenv("API_URL")
if public_url:
agent_id = get_agent_id()
register_with_registry(agent_id, public_url, api_url)
register_with_registry(agent_id, public_url, api_url, agent_facts=_load_agent_facts_from_env())
else:
print("WARNING: PUBLIC_URL environment variable not set. Agent will not be registered.")

Expand Down
12 changes: 10 additions & 2 deletions nanda_adapter/core/nanda.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@
class NANDA:
"""NANDA class to create agent_bridge with custom improvement logic"""

def __init__(self, improvement_logic):
def __init__(self, improvement_logic, agent_facts=None):
"""
Initialize NANDA with custom improvement logic

Args:
improvement_logic: Function that takes (message_text: str) -> str
agent_facts: Optional dict sent to the registry alongside the
agent_id/agent_url/api_url. See projnanda/agentfacts for
the schema.
"""
self.improvement_logic = improvement_logic
self.agent_facts = agent_facts
self.bridge = None
print(f"🤖 NANDA initialized with custom improvement logic: {improvement_logic.__name__}")

Expand All @@ -44,6 +48,10 @@ def __init__(self, improvement_logic):

# Create agent bridge with custom logic
self.create_agent_bridge()

def set_agent_facts(self, agent_facts):
"""Set agent_facts to send at registration"""
self.agent_facts = agent_facts

def register_custom_improver(self):
"""Register the custom improvement logic with agent_bridge"""
Expand Down Expand Up @@ -88,7 +96,7 @@ def start_server(self):
# os.environ["UI_CLIENT_URL"] = f"{api_url}/api/receive_message"

if public_url:
register_with_registry(agent_id, public_url, api_url)
register_with_registry(agent_id, public_url, api_url, agent_facts=self.agent_facts)
else:
print("WARNING: PUBLIC_URL environment variable not set. Agent will not be registered.")

Expand Down
Empty file added tests/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions tests/test_register_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Verify the registration payload includes agent_facts when supplied."""
import os
import sys
import unittest
from unittest import mock

# Ensure the package is importable when running this file directly
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from nanda_adapter.core import agent_bridge


class _FakeResponse:
def __init__(self, status_code=200, text="ok"):
self.status_code = status_code
self.text = text


class RegisterPayloadTest(unittest.TestCase):

def test_payload_omits_agent_facts_by_default(self):
captured = {}

def fake_post(url, json=None, **kwargs):
captured["url"] = url
captured["json"] = json
return _FakeResponse()

with mock.patch.object(agent_bridge.requests, "post", side_effect=fake_post):
ok = agent_bridge.register_with_registry(
"agent-x", "https://x.example.com", "https://api.example.com"
)

self.assertTrue(ok)
self.assertNotIn("agent_facts", captured["json"])
self.assertEqual(captured["json"]["agent_id"], "agent-x")

def test_payload_includes_agent_facts_when_provided(self):
captured = {}

def fake_post(url, json=None, **kwargs):
captured["json"] = json
return _FakeResponse()

facts = {
"agent_name": "pirate-bot",
"skills": [{"id": "translate.pirate"}],
}

with mock.patch.object(agent_bridge.requests, "post", side_effect=fake_post):
agent_bridge.register_with_registry(
"agent-x",
"https://x.example.com",
"https://api.example.com",
agent_facts=facts,
)

self.assertEqual(captured["json"]["agent_facts"], facts)


if __name__ == "__main__":
unittest.main()