autogen-xache 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autogen_xache-0.1.0.dist-info/METADATA +297 -0
- autogen_xache-0.1.0.dist-info/RECORD +9 -0
- autogen_xache-0.1.0.dist-info/WHEEL +5 -0
- autogen_xache-0.1.0.dist-info/top_level.txt +1 -0
- xache_autogen/__init__.py +51 -0
- xache_autogen/_async_utils.py +55 -0
- xache_autogen/agent.py +300 -0
- xache_autogen/functions.py +393 -0
- xache_autogen/memory.py +243 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autogen-xache
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AutoGen integration for Xache Protocol - verifiable AI agent memory
|
|
5
|
+
Author-email: Xache Protocol <dev@xache.xyz>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://xache.xyz
|
|
8
|
+
Project-URL: Documentation, https://docs.xache.xyz
|
|
9
|
+
Project-URL: Repository, https://github.com/oliveskin/xache
|
|
10
|
+
Keywords: autogen,ai,agents,memory,multi-agent,blockchain,receipts,reputation,erc8004,x402
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: xache>=0.1.0
|
|
24
|
+
Requires-Dist: pyautogen>=0.2.0
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# autogen-xache
|
|
33
|
+
|
|
34
|
+
AutoGen integration for [Xache Protocol](https://xache.xyz) - verifiable AI agent memory with cryptographic receipts, collective intelligence, and portable ERC-8004 reputation.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install autogen-xache
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Create an Agent with Xache Memory
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from autogen import UserProxyAgent
|
|
48
|
+
from xache_autogen import XacheAssistantAgent
|
|
49
|
+
|
|
50
|
+
# Create an assistant with Xache capabilities
|
|
51
|
+
assistant = XacheAssistantAgent(
|
|
52
|
+
name="assistant",
|
|
53
|
+
wallet_address="0x...",
|
|
54
|
+
private_key="0x...",
|
|
55
|
+
llm_config={"model": "gpt-4"}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Create a user proxy
|
|
59
|
+
user_proxy = UserProxyAgent(
|
|
60
|
+
name="user",
|
|
61
|
+
human_input_mode="TERMINATE"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Start conversation
|
|
65
|
+
user_proxy.initiate_chat(
|
|
66
|
+
assistant,
|
|
67
|
+
message="Research quantum computing and remember the key findings"
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Add Xache Functions to Any Agent
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from autogen import AssistantAgent
|
|
75
|
+
from xache_autogen import xache_functions
|
|
76
|
+
|
|
77
|
+
# Add Xache functions to LLM config
|
|
78
|
+
llm_config = {
|
|
79
|
+
"model": "gpt-4",
|
|
80
|
+
"functions": xache_functions
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
agent = AssistantAgent(
|
|
84
|
+
name="researcher",
|
|
85
|
+
llm_config=llm_config
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
### Available Functions
|
|
92
|
+
|
|
93
|
+
The `xache_functions` list provides these capabilities:
|
|
94
|
+
|
|
95
|
+
#### Memory Functions
|
|
96
|
+
- **xache_memory_store** - Store information with cryptographic receipts
|
|
97
|
+
- **xache_memory_retrieve** - Retrieve stored memories by semantic search
|
|
98
|
+
|
|
99
|
+
#### Collective Intelligence Functions
|
|
100
|
+
- **xache_collective_contribute** - Share insights with other agents
|
|
101
|
+
- **xache_collective_query** - Learn from community knowledge
|
|
102
|
+
|
|
103
|
+
#### Reputation Functions
|
|
104
|
+
- **xache_check_reputation** - View reputation score and ERC-8004 status
|
|
105
|
+
|
|
106
|
+
### Agent Types
|
|
107
|
+
|
|
108
|
+
#### XacheMemoryAgent
|
|
109
|
+
|
|
110
|
+
Basic conversable agent with Xache capabilities:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from xache_autogen import XacheMemoryAgent
|
|
114
|
+
|
|
115
|
+
agent = XacheMemoryAgent(
|
|
116
|
+
name="researcher",
|
|
117
|
+
wallet_address="0x...",
|
|
118
|
+
private_key="0x...",
|
|
119
|
+
llm_config={"model": "gpt-4"}
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### XacheAssistantAgent
|
|
124
|
+
|
|
125
|
+
Extended AssistantAgent with Xache capabilities:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from xache_autogen import XacheAssistantAgent
|
|
129
|
+
|
|
130
|
+
assistant = XacheAssistantAgent(
|
|
131
|
+
name="assistant",
|
|
132
|
+
wallet_address="0x...",
|
|
133
|
+
private_key="0x...",
|
|
134
|
+
system_message="You are a helpful assistant with persistent memory.",
|
|
135
|
+
llm_config={"model": "gpt-4"}
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Conversation Memory
|
|
140
|
+
|
|
141
|
+
Store and retrieve conversation history:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from xache_autogen import XacheConversationMemory
|
|
145
|
+
|
|
146
|
+
memory = XacheConversationMemory(
|
|
147
|
+
wallet_address="0x...",
|
|
148
|
+
private_key="0x...",
|
|
149
|
+
conversation_id="unique-session-id"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Add messages
|
|
153
|
+
memory.add_message("user", "Hello!")
|
|
154
|
+
memory.add_message("assistant", "Hi there! How can I help?")
|
|
155
|
+
|
|
156
|
+
# Get history
|
|
157
|
+
history = memory.get_history()
|
|
158
|
+
|
|
159
|
+
# Store a summary
|
|
160
|
+
memory.store_summary("User greeted the assistant.")
|
|
161
|
+
|
|
162
|
+
# Search past conversations
|
|
163
|
+
results = memory.search("quantum computing")
|
|
164
|
+
|
|
165
|
+
# Format for prompt
|
|
166
|
+
context = memory.format_for_prompt(max_messages=5)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Multi-Agent Conversations
|
|
170
|
+
|
|
171
|
+
Xache works seamlessly with multi-agent setups:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from autogen import UserProxyAgent, GroupChat, GroupChatManager
|
|
175
|
+
from xache_autogen import XacheAssistantAgent
|
|
176
|
+
|
|
177
|
+
# Shared wallet = shared memory
|
|
178
|
+
config = {
|
|
179
|
+
"wallet_address": "0x...",
|
|
180
|
+
"private_key": "0x...",
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
researcher = XacheAssistantAgent(
|
|
184
|
+
name="researcher",
|
|
185
|
+
system_message="You research topics and store findings.",
|
|
186
|
+
llm_config={"model": "gpt-4"},
|
|
187
|
+
**config
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
writer = XacheAssistantAgent(
|
|
191
|
+
name="writer",
|
|
192
|
+
system_message="You write articles based on research.",
|
|
193
|
+
llm_config={"model": "gpt-4"},
|
|
194
|
+
**config
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
user_proxy = UserProxyAgent(name="user")
|
|
198
|
+
|
|
199
|
+
# Create group chat
|
|
200
|
+
groupchat = GroupChat(
|
|
201
|
+
agents=[user_proxy, researcher, writer],
|
|
202
|
+
messages=[],
|
|
203
|
+
max_round=10
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
manager = GroupChatManager(
|
|
207
|
+
groupchat=groupchat,
|
|
208
|
+
llm_config={"model": "gpt-4"}
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Both agents share the same memory pool
|
|
212
|
+
user_proxy.initiate_chat(
|
|
213
|
+
manager,
|
|
214
|
+
message="Research AI safety and write an article"
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Direct Function Usage
|
|
219
|
+
|
|
220
|
+
Use Xache functions directly outside agents:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from xache_autogen import (
|
|
224
|
+
memory_store,
|
|
225
|
+
memory_retrieve,
|
|
226
|
+
collective_contribute,
|
|
227
|
+
collective_query,
|
|
228
|
+
check_reputation,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
config = {
|
|
232
|
+
"wallet_address": "0x...",
|
|
233
|
+
"private_key": "0x...",
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# Store a memory
|
|
237
|
+
result = memory_store(
|
|
238
|
+
content="Important finding about quantum computing",
|
|
239
|
+
context="research",
|
|
240
|
+
tags=["quantum", "computing"],
|
|
241
|
+
**config
|
|
242
|
+
)
|
|
243
|
+
print(f"Stored: {result['memoryId']}")
|
|
244
|
+
|
|
245
|
+
# Retrieve memories
|
|
246
|
+
memories = memory_retrieve(
|
|
247
|
+
query="quantum computing",
|
|
248
|
+
limit=5,
|
|
249
|
+
**config
|
|
250
|
+
)
|
|
251
|
+
print(f"Found {memories['count']} memories")
|
|
252
|
+
|
|
253
|
+
# Contribute to collective
|
|
254
|
+
collective_contribute(
|
|
255
|
+
insight="Quantum computers excel at optimization problems",
|
|
256
|
+
domain="quantum-computing",
|
|
257
|
+
evidence="Research paper XYZ",
|
|
258
|
+
**config
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Query collective
|
|
262
|
+
insights = collective_query(
|
|
263
|
+
query="quantum computing applications",
|
|
264
|
+
domain="quantum-computing",
|
|
265
|
+
**config
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Check reputation
|
|
269
|
+
rep = check_reputation(**config)
|
|
270
|
+
print(f"Reputation: {rep['score']} ({rep['level']})")
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Pricing
|
|
274
|
+
|
|
275
|
+
All operations use x402 micropayments (auto-handled):
|
|
276
|
+
|
|
277
|
+
| Operation | Price |
|
|
278
|
+
|-----------|-------|
|
|
279
|
+
| Memory Store | $0.002 |
|
|
280
|
+
| Memory Retrieve | $0.003 |
|
|
281
|
+
| Collective Contribute | $0.002 |
|
|
282
|
+
| Collective Query | $0.011 |
|
|
283
|
+
|
|
284
|
+
## ERC-8004 Portable Reputation
|
|
285
|
+
|
|
286
|
+
Your agents build reputation through quality contributions and payments. Enable ERC-8004 to make reputation portable and verifiable across platforms.
|
|
287
|
+
|
|
288
|
+
## Resources
|
|
289
|
+
|
|
290
|
+
- [Documentation](https://docs.xache.xyz)
|
|
291
|
+
- [API Reference](https://docs.xache.xyz/api)
|
|
292
|
+
- [GitHub](https://github.com/oliveskin/xache)
|
|
293
|
+
- [Discord](https://discord.gg/xache)
|
|
294
|
+
|
|
295
|
+
## License
|
|
296
|
+
|
|
297
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
xache_autogen/__init__.py,sha256=s4Lj9MXFImvo7KZiV0x6DRfAK1du3R4Zxor2EGvYiNM,1242
|
|
2
|
+
xache_autogen/_async_utils.py,sha256=5J3Thx1YzGHnCSVXAbxf5geuARbIJsHy__XVNPAdKj0,1635
|
|
3
|
+
xache_autogen/agent.py,sha256=Ns62M837HBL_fjv5vDvg9VjazgsWD0dPrqUMD3H-TpE,10223
|
|
4
|
+
xache_autogen/functions.py,sha256=yx-NPPPfAhR_7WoAEGYIA94j9scWX8rq9QEOQfaDnN0,11323
|
|
5
|
+
xache_autogen/memory.py,sha256=VS-fUbeMWil5pjQUGFLNXGUGvjtIMJQXkHkIGgNWvzk,6903
|
|
6
|
+
autogen_xache-0.1.0.dist-info/METADATA,sha256=zIpN_8Z9J8-t1R0TUwyVjec0mS0cItXC11YlH3_rHl8,7356
|
|
7
|
+
autogen_xache-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
autogen_xache-0.1.0.dist-info/top_level.txt,sha256=6Hsa4sEsdRw_49EhDw7-VWMRLgg7RYv2q02ozjF8S9Q,14
|
|
9
|
+
autogen_xache-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
xache_autogen
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoGen integration for Xache Protocol
|
|
3
|
+
Verifiable memory, collective intelligence, and reputation for multi-agent conversations
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
```python
|
|
7
|
+
from autogen import AssistantAgent, UserProxyAgent
|
|
8
|
+
from xache_autogen import XacheMemoryAgent, xache_functions
|
|
9
|
+
|
|
10
|
+
# Create an agent with Xache memory capabilities
|
|
11
|
+
assistant = XacheMemoryAgent(
|
|
12
|
+
name="assistant",
|
|
13
|
+
wallet_address="0x...",
|
|
14
|
+
private_key="0x...",
|
|
15
|
+
llm_config={"model": "gpt-4"}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Or add Xache functions to any agent
|
|
19
|
+
agent = AssistantAgent(
|
|
20
|
+
name="researcher",
|
|
21
|
+
llm_config={"model": "gpt-4", "functions": xache_functions}
|
|
22
|
+
)
|
|
23
|
+
```
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from .agent import XacheMemoryAgent, XacheAssistantAgent
|
|
27
|
+
from .functions import (
|
|
28
|
+
xache_functions,
|
|
29
|
+
memory_store,
|
|
30
|
+
memory_retrieve,
|
|
31
|
+
collective_contribute,
|
|
32
|
+
collective_query,
|
|
33
|
+
check_reputation,
|
|
34
|
+
)
|
|
35
|
+
from .memory import XacheConversationMemory
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
__all__ = [
|
|
39
|
+
# Agents
|
|
40
|
+
"XacheMemoryAgent",
|
|
41
|
+
"XacheAssistantAgent",
|
|
42
|
+
# Functions
|
|
43
|
+
"xache_functions",
|
|
44
|
+
"memory_store",
|
|
45
|
+
"memory_retrieve",
|
|
46
|
+
"collective_contribute",
|
|
47
|
+
"collective_query",
|
|
48
|
+
"check_reputation",
|
|
49
|
+
# Memory
|
|
50
|
+
"XacheConversationMemory",
|
|
51
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async utilities for running coroutines in any context.
|
|
3
|
+
Handles Jupyter notebooks, async frameworks, and sync contexts.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import TypeVar, Coroutine, Any
|
|
8
|
+
|
|
9
|
+
T = TypeVar('T')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_sync(coro: Coroutine[Any, Any, T]) -> T:
|
|
13
|
+
"""
|
|
14
|
+
Run an async coroutine synchronously in any context.
|
|
15
|
+
|
|
16
|
+
Works correctly in:
|
|
17
|
+
- Regular sync Python scripts
|
|
18
|
+
- Jupyter notebooks (where event loop is already running)
|
|
19
|
+
- Async frameworks (FastAPI, etc.)
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
coro: The coroutine to run
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The result of the coroutine
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
# Check if there's already a running event loop
|
|
29
|
+
loop = asyncio.get_running_loop()
|
|
30
|
+
except RuntimeError:
|
|
31
|
+
# No running loop - we can safely use asyncio.run()
|
|
32
|
+
return asyncio.run(coro)
|
|
33
|
+
|
|
34
|
+
# There's a running loop - we need to handle this carefully
|
|
35
|
+
try:
|
|
36
|
+
# Try using nest_asyncio for Jupyter compatibility
|
|
37
|
+
import nest_asyncio
|
|
38
|
+
nest_asyncio.apply()
|
|
39
|
+
return loop.run_until_complete(coro)
|
|
40
|
+
except ImportError:
|
|
41
|
+
# nest_asyncio not installed - use thread pool as fallback
|
|
42
|
+
import concurrent.futures
|
|
43
|
+
|
|
44
|
+
def _run_in_thread():
|
|
45
|
+
# Create a new event loop for this thread
|
|
46
|
+
new_loop = asyncio.new_event_loop()
|
|
47
|
+
asyncio.set_event_loop(new_loop)
|
|
48
|
+
try:
|
|
49
|
+
return new_loop.run_until_complete(coro)
|
|
50
|
+
finally:
|
|
51
|
+
new_loop.close()
|
|
52
|
+
|
|
53
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
54
|
+
future = pool.submit(_run_in_thread)
|
|
55
|
+
return future.result()
|
xache_autogen/agent.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Xache-enabled Agents for AutoGen
|
|
3
|
+
Agents with built-in Xache memory and collective intelligence capabilities
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
8
|
+
from functools import partial
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from autogen import AssistantAgent, ConversableAgent
|
|
12
|
+
except ImportError:
|
|
13
|
+
raise ImportError(
|
|
14
|
+
"AutoGen is required. Install it with: pip install pyautogen"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .functions import (
|
|
18
|
+
memory_store,
|
|
19
|
+
memory_retrieve,
|
|
20
|
+
collective_contribute,
|
|
21
|
+
collective_query,
|
|
22
|
+
check_reputation,
|
|
23
|
+
xache_functions,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class XacheMemoryAgent(ConversableAgent):
|
|
28
|
+
"""
|
|
29
|
+
AutoGen agent with built-in Xache memory capabilities.
|
|
30
|
+
|
|
31
|
+
This agent automatically has access to Xache memory, collective intelligence,
|
|
32
|
+
and reputation functions.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
```python
|
|
36
|
+
from xache_autogen import XacheMemoryAgent
|
|
37
|
+
|
|
38
|
+
agent = XacheMemoryAgent(
|
|
39
|
+
name="researcher",
|
|
40
|
+
wallet_address="0x...",
|
|
41
|
+
private_key="0x...",
|
|
42
|
+
llm_config={"model": "gpt-4"}
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
name: str,
|
|
50
|
+
wallet_address: str,
|
|
51
|
+
private_key: str,
|
|
52
|
+
api_url: Optional[str] = None,
|
|
53
|
+
chain: str = "base",
|
|
54
|
+
system_message: Optional[str] = None,
|
|
55
|
+
llm_config: Optional[Dict] = None,
|
|
56
|
+
timeout: int = 30000,
|
|
57
|
+
debug: bool = False,
|
|
58
|
+
**kwargs
|
|
59
|
+
):
|
|
60
|
+
# Build xache config with env-based default
|
|
61
|
+
resolved_api_url = api_url or os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
|
|
62
|
+
self._xache_config = {
|
|
63
|
+
"wallet_address": wallet_address,
|
|
64
|
+
"private_key": private_key,
|
|
65
|
+
"api_url": resolved_api_url,
|
|
66
|
+
"chain": chain,
|
|
67
|
+
"timeout": timeout,
|
|
68
|
+
"debug": debug,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Create bound function map
|
|
72
|
+
self._xache_function_map = self._create_function_map()
|
|
73
|
+
|
|
74
|
+
# Add Xache functions to llm_config (with deduplication)
|
|
75
|
+
if llm_config is None:
|
|
76
|
+
llm_config = {}
|
|
77
|
+
|
|
78
|
+
if "functions" not in llm_config:
|
|
79
|
+
llm_config["functions"] = []
|
|
80
|
+
|
|
81
|
+
# Deduplicate: only add xache functions that aren't already present
|
|
82
|
+
existing_names = {f.get("name") for f in llm_config["functions"]}
|
|
83
|
+
for func in xache_functions:
|
|
84
|
+
if func.get("name") not in existing_names:
|
|
85
|
+
llm_config["functions"].append(func)
|
|
86
|
+
existing_names.add(func.get("name"))
|
|
87
|
+
|
|
88
|
+
# Default system message
|
|
89
|
+
if system_message is None:
|
|
90
|
+
system_message = (
|
|
91
|
+
"You are a helpful assistant with access to persistent memory "
|
|
92
|
+
"and collective intelligence through Xache Protocol. "
|
|
93
|
+
"You can store important information, retrieve past memories, "
|
|
94
|
+
"contribute insights to help other agents, and query the collective "
|
|
95
|
+
"knowledge pool."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
super().__init__(
|
|
99
|
+
name=name,
|
|
100
|
+
system_message=system_message,
|
|
101
|
+
llm_config=llm_config,
|
|
102
|
+
**kwargs
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Register function executors
|
|
106
|
+
self._register_xache_functions()
|
|
107
|
+
|
|
108
|
+
def _create_function_map(self) -> Dict[str, Callable]:
|
|
109
|
+
"""Create bound functions with Xache config"""
|
|
110
|
+
config = self._xache_config
|
|
111
|
+
return {
|
|
112
|
+
"xache_memory_store": partial(
|
|
113
|
+
memory_store,
|
|
114
|
+
wallet_address=config["wallet_address"],
|
|
115
|
+
private_key=config["private_key"],
|
|
116
|
+
api_url=config["api_url"],
|
|
117
|
+
chain=config["chain"],
|
|
118
|
+
),
|
|
119
|
+
"xache_memory_retrieve": partial(
|
|
120
|
+
memory_retrieve,
|
|
121
|
+
wallet_address=config["wallet_address"],
|
|
122
|
+
private_key=config["private_key"],
|
|
123
|
+
api_url=config["api_url"],
|
|
124
|
+
chain=config["chain"],
|
|
125
|
+
),
|
|
126
|
+
"xache_collective_contribute": partial(
|
|
127
|
+
collective_contribute,
|
|
128
|
+
wallet_address=config["wallet_address"],
|
|
129
|
+
private_key=config["private_key"],
|
|
130
|
+
api_url=config["api_url"],
|
|
131
|
+
chain=config["chain"],
|
|
132
|
+
),
|
|
133
|
+
"xache_collective_query": partial(
|
|
134
|
+
collective_query,
|
|
135
|
+
wallet_address=config["wallet_address"],
|
|
136
|
+
private_key=config["private_key"],
|
|
137
|
+
api_url=config["api_url"],
|
|
138
|
+
chain=config["chain"],
|
|
139
|
+
),
|
|
140
|
+
"xache_check_reputation": partial(
|
|
141
|
+
check_reputation,
|
|
142
|
+
wallet_address=config["wallet_address"],
|
|
143
|
+
private_key=config["private_key"],
|
|
144
|
+
api_url=config["api_url"],
|
|
145
|
+
chain=config["chain"],
|
|
146
|
+
),
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def _register_xache_functions(self):
|
|
150
|
+
"""Register Xache functions as executable"""
|
|
151
|
+
for name, func in self._xache_function_map.items():
|
|
152
|
+
self.register_function(
|
|
153
|
+
function_map={name: func}
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def execute_function(self, func_call: Dict[str, Any]) -> Any:
|
|
157
|
+
"""Execute a function call"""
|
|
158
|
+
name = func_call.get("name")
|
|
159
|
+
args = func_call.get("arguments", {})
|
|
160
|
+
|
|
161
|
+
if name in self._xache_function_map:
|
|
162
|
+
return self._xache_function_map[name](**args)
|
|
163
|
+
|
|
164
|
+
return super().execute_function(func_call)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class XacheAssistantAgent(AssistantAgent):
|
|
168
|
+
"""
|
|
169
|
+
AutoGen AssistantAgent with Xache capabilities.
|
|
170
|
+
|
|
171
|
+
Extends AssistantAgent with persistent memory and collective intelligence.
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
```python
|
|
175
|
+
from xache_autogen import XacheAssistantAgent
|
|
176
|
+
|
|
177
|
+
assistant = XacheAssistantAgent(
|
|
178
|
+
name="assistant",
|
|
179
|
+
wallet_address="0x...",
|
|
180
|
+
private_key="0x...",
|
|
181
|
+
llm_config={"model": "gpt-4"}
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Use in conversation
|
|
185
|
+
user_proxy.initiate_chat(assistant, message="Research quantum computing")
|
|
186
|
+
```
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
def __init__(
|
|
190
|
+
self,
|
|
191
|
+
name: str,
|
|
192
|
+
wallet_address: str,
|
|
193
|
+
private_key: str,
|
|
194
|
+
api_url: Optional[str] = None,
|
|
195
|
+
chain: str = "base",
|
|
196
|
+
system_message: Optional[str] = None,
|
|
197
|
+
llm_config: Optional[Dict] = None,
|
|
198
|
+
timeout: int = 30000,
|
|
199
|
+
debug: bool = False,
|
|
200
|
+
**kwargs
|
|
201
|
+
):
|
|
202
|
+
# Build xache config with env-based default
|
|
203
|
+
resolved_api_url = api_url or os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
|
|
204
|
+
self._xache_config = {
|
|
205
|
+
"wallet_address": wallet_address,
|
|
206
|
+
"private_key": private_key,
|
|
207
|
+
"api_url": resolved_api_url,
|
|
208
|
+
"chain": chain,
|
|
209
|
+
"timeout": timeout,
|
|
210
|
+
"debug": debug,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# Create bound function map
|
|
214
|
+
self._xache_function_map = self._create_function_map()
|
|
215
|
+
|
|
216
|
+
# Add Xache functions to llm_config (with deduplication)
|
|
217
|
+
if llm_config is None:
|
|
218
|
+
llm_config = {}
|
|
219
|
+
|
|
220
|
+
if "functions" not in llm_config:
|
|
221
|
+
llm_config["functions"] = []
|
|
222
|
+
|
|
223
|
+
# Deduplicate: only add xache functions that aren't already present
|
|
224
|
+
existing_names = {f.get("name") for f in llm_config["functions"]}
|
|
225
|
+
for func in xache_functions:
|
|
226
|
+
if func.get("name") not in existing_names:
|
|
227
|
+
llm_config["functions"].append(func)
|
|
228
|
+
existing_names.add(func.get("name"))
|
|
229
|
+
|
|
230
|
+
# Default system message
|
|
231
|
+
if system_message is None:
|
|
232
|
+
system_message = (
|
|
233
|
+
"You are a helpful AI assistant with persistent memory through "
|
|
234
|
+
"Xache Protocol. You can:\n"
|
|
235
|
+
"- Store important information with xache_memory_store\n"
|
|
236
|
+
"- Retrieve past memories with xache_memory_retrieve\n"
|
|
237
|
+
"- Contribute insights with xache_collective_contribute\n"
|
|
238
|
+
"- Learn from others with xache_collective_query\n"
|
|
239
|
+
"- Check reputation with xache_check_reputation\n\n"
|
|
240
|
+
"Use these capabilities to maintain context across conversations "
|
|
241
|
+
"and contribute to collective knowledge."
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
super().__init__(
|
|
245
|
+
name=name,
|
|
246
|
+
system_message=system_message,
|
|
247
|
+
llm_config=llm_config,
|
|
248
|
+
**kwargs
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Register function executors
|
|
252
|
+
self._register_xache_functions()
|
|
253
|
+
|
|
254
|
+
def _create_function_map(self) -> Dict[str, Callable]:
|
|
255
|
+
"""Create bound functions with Xache config"""
|
|
256
|
+
config = self._xache_config
|
|
257
|
+
return {
|
|
258
|
+
"xache_memory_store": partial(
|
|
259
|
+
memory_store,
|
|
260
|
+
wallet_address=config["wallet_address"],
|
|
261
|
+
private_key=config["private_key"],
|
|
262
|
+
api_url=config["api_url"],
|
|
263
|
+
chain=config["chain"],
|
|
264
|
+
),
|
|
265
|
+
"xache_memory_retrieve": partial(
|
|
266
|
+
memory_retrieve,
|
|
267
|
+
wallet_address=config["wallet_address"],
|
|
268
|
+
private_key=config["private_key"],
|
|
269
|
+
api_url=config["api_url"],
|
|
270
|
+
chain=config["chain"],
|
|
271
|
+
),
|
|
272
|
+
"xache_collective_contribute": partial(
|
|
273
|
+
collective_contribute,
|
|
274
|
+
wallet_address=config["wallet_address"],
|
|
275
|
+
private_key=config["private_key"],
|
|
276
|
+
api_url=config["api_url"],
|
|
277
|
+
chain=config["chain"],
|
|
278
|
+
),
|
|
279
|
+
"xache_collective_query": partial(
|
|
280
|
+
collective_query,
|
|
281
|
+
wallet_address=config["wallet_address"],
|
|
282
|
+
private_key=config["private_key"],
|
|
283
|
+
api_url=config["api_url"],
|
|
284
|
+
chain=config["chain"],
|
|
285
|
+
),
|
|
286
|
+
"xache_check_reputation": partial(
|
|
287
|
+
check_reputation,
|
|
288
|
+
wallet_address=config["wallet_address"],
|
|
289
|
+
private_key=config["private_key"],
|
|
290
|
+
api_url=config["api_url"],
|
|
291
|
+
chain=config["chain"],
|
|
292
|
+
),
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
def _register_xache_functions(self):
|
|
296
|
+
"""Register Xache functions as executable"""
|
|
297
|
+
for name, func in self._xache_function_map.items():
|
|
298
|
+
self.register_function(
|
|
299
|
+
function_map={name: func}
|
|
300
|
+
)
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Xache Functions for AutoGen
|
|
3
|
+
Function definitions that can be registered with AutoGen agents
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any, Dict, List, Optional, Callable
|
|
8
|
+
|
|
9
|
+
from xache import XacheClient
|
|
10
|
+
from ._async_utils import run_sync
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_xache_client(
|
|
14
|
+
wallet_address: str,
|
|
15
|
+
private_key: str,
|
|
16
|
+
api_url: Optional[str] = None,
|
|
17
|
+
chain: str = "base",
|
|
18
|
+
) -> XacheClient:
|
|
19
|
+
"""Create an Xache client instance"""
|
|
20
|
+
chain_prefix = "sol" if chain == "solana" else "evm"
|
|
21
|
+
did = f"did:agent:{chain_prefix}:{wallet_address.lower()}"
|
|
22
|
+
resolved_api_url = api_url or os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
|
|
23
|
+
return XacheClient(
|
|
24
|
+
api_url=resolved_api_url,
|
|
25
|
+
did=did,
|
|
26
|
+
private_key=private_key,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def memory_store(
|
|
31
|
+
content: str,
|
|
32
|
+
context: str,
|
|
33
|
+
tags: Optional[List[str]] = None,
|
|
34
|
+
*,
|
|
35
|
+
wallet_address: str,
|
|
36
|
+
private_key: str,
|
|
37
|
+
api_url: Optional[str] = None,
|
|
38
|
+
chain: str = "base",
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""
|
|
41
|
+
Store a memory with cryptographic receipt.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
content: The content to store
|
|
45
|
+
context: Context/category for the memory
|
|
46
|
+
tags: Optional tags for categorization
|
|
47
|
+
wallet_address: Wallet address for authentication
|
|
48
|
+
private_key: Private key for signing
|
|
49
|
+
api_url: Xache API URL
|
|
50
|
+
chain: Chain to use ('base' or 'solana')
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dict with memoryId and receiptId
|
|
54
|
+
"""
|
|
55
|
+
client = create_xache_client(wallet_address, private_key, api_url, chain)
|
|
56
|
+
|
|
57
|
+
async def _store():
|
|
58
|
+
async with client as c:
|
|
59
|
+
result = await c.memory.store(
|
|
60
|
+
content=content,
|
|
61
|
+
context=context,
|
|
62
|
+
tags=tags or [],
|
|
63
|
+
)
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
result = run_sync(_store())
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"memoryId": result.get("memoryId"),
|
|
70
|
+
"receiptId": result.get("receiptId"),
|
|
71
|
+
"message": f"Stored memory in context '{context}'"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def memory_retrieve(
|
|
76
|
+
query: str,
|
|
77
|
+
context: Optional[str] = None,
|
|
78
|
+
limit: int = 5,
|
|
79
|
+
*,
|
|
80
|
+
wallet_address: str,
|
|
81
|
+
private_key: str,
|
|
82
|
+
api_url: Optional[str] = None,
|
|
83
|
+
chain: str = "base",
|
|
84
|
+
) -> Dict[str, Any]:
|
|
85
|
+
"""
|
|
86
|
+
Retrieve memories by semantic search.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
query: Search query
|
|
90
|
+
context: Optional context filter
|
|
91
|
+
limit: Maximum number of results
|
|
92
|
+
wallet_address: Wallet address for authentication
|
|
93
|
+
private_key: Private key for signing
|
|
94
|
+
api_url: Xache API URL
|
|
95
|
+
chain: Chain to use ('base' or 'solana')
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dict with memories list
|
|
99
|
+
"""
|
|
100
|
+
client = create_xache_client(wallet_address, private_key, api_url, chain)
|
|
101
|
+
|
|
102
|
+
async def _retrieve():
|
|
103
|
+
async with client as c:
|
|
104
|
+
result = await c.memory.retrieve(
|
|
105
|
+
query=query,
|
|
106
|
+
context=context,
|
|
107
|
+
limit=limit,
|
|
108
|
+
)
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
result = run_sync(_retrieve())
|
|
112
|
+
|
|
113
|
+
# Defensive type checking
|
|
114
|
+
memories = result.get("memories", []) if isinstance(result, dict) else []
|
|
115
|
+
return {
|
|
116
|
+
"count": len(memories),
|
|
117
|
+
"memories": [
|
|
118
|
+
{
|
|
119
|
+
"content": m.get("content"),
|
|
120
|
+
"context": m.get("context"),
|
|
121
|
+
"relevance": m.get("relevance"),
|
|
122
|
+
}
|
|
123
|
+
for m in memories
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def collective_contribute(
|
|
129
|
+
insight: str,
|
|
130
|
+
domain: str,
|
|
131
|
+
evidence: Optional[str] = None,
|
|
132
|
+
tags: Optional[List[str]] = None,
|
|
133
|
+
*,
|
|
134
|
+
wallet_address: str,
|
|
135
|
+
private_key: str,
|
|
136
|
+
api_url: Optional[str] = None,
|
|
137
|
+
chain: str = "base",
|
|
138
|
+
) -> Dict[str, Any]:
|
|
139
|
+
"""
|
|
140
|
+
Contribute an insight to collective intelligence.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
insight: The insight to contribute
|
|
144
|
+
domain: Domain/topic of the insight
|
|
145
|
+
evidence: Optional supporting evidence
|
|
146
|
+
tags: Optional tags
|
|
147
|
+
wallet_address: Wallet address for authentication
|
|
148
|
+
private_key: Private key for signing
|
|
149
|
+
api_url: Xache API URL
|
|
150
|
+
chain: Chain to use ('base' or 'solana')
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Dict with heuristicId and receiptId
|
|
154
|
+
"""
|
|
155
|
+
client = create_xache_client(wallet_address, private_key, api_url, chain)
|
|
156
|
+
|
|
157
|
+
async def _contribute():
|
|
158
|
+
async with client as c:
|
|
159
|
+
result = await c.collective.contribute(
|
|
160
|
+
domain=domain,
|
|
161
|
+
pattern=insight,
|
|
162
|
+
evidence=evidence,
|
|
163
|
+
tags=tags or [],
|
|
164
|
+
)
|
|
165
|
+
return result
|
|
166
|
+
|
|
167
|
+
result = run_sync(_contribute())
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
"heuristicId": result.get("heuristicId"),
|
|
171
|
+
"receiptId": result.get("receiptId"),
|
|
172
|
+
"message": f"Contributed insight to domain '{domain}'"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def collective_query(
|
|
177
|
+
query: str,
|
|
178
|
+
domain: Optional[str] = None,
|
|
179
|
+
limit: int = 5,
|
|
180
|
+
*,
|
|
181
|
+
wallet_address: str,
|
|
182
|
+
private_key: str,
|
|
183
|
+
api_url: Optional[str] = None,
|
|
184
|
+
chain: str = "base",
|
|
185
|
+
) -> Dict[str, Any]:
|
|
186
|
+
"""
|
|
187
|
+
Query collective intelligence for insights.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
query: Search query
|
|
191
|
+
domain: Optional domain filter
|
|
192
|
+
limit: Maximum number of results
|
|
193
|
+
wallet_address: Wallet address for authentication
|
|
194
|
+
private_key: Private key for signing
|
|
195
|
+
api_url: Xache API URL
|
|
196
|
+
chain: Chain to use ('base' or 'solana')
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Dict with insights list
|
|
200
|
+
"""
|
|
201
|
+
client = create_xache_client(wallet_address, private_key, api_url, chain)
|
|
202
|
+
|
|
203
|
+
async def _query():
|
|
204
|
+
async with client as c:
|
|
205
|
+
result = await c.collective.query(
|
|
206
|
+
query=query,
|
|
207
|
+
domain=domain,
|
|
208
|
+
limit=limit,
|
|
209
|
+
)
|
|
210
|
+
return result
|
|
211
|
+
|
|
212
|
+
result = run_sync(_query())
|
|
213
|
+
|
|
214
|
+
# Defensive type checking
|
|
215
|
+
results = result.get("results", []) if isinstance(result, dict) else []
|
|
216
|
+
return {
|
|
217
|
+
"count": len(results),
|
|
218
|
+
"insights": [
|
|
219
|
+
{
|
|
220
|
+
"pattern": r.get("pattern"),
|
|
221
|
+
"domain": r.get("domain"),
|
|
222
|
+
"relevance": r.get("relevance"),
|
|
223
|
+
}
|
|
224
|
+
for r in results
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def check_reputation(
|
|
230
|
+
agent_did: Optional[str] = None,
|
|
231
|
+
*,
|
|
232
|
+
wallet_address: str,
|
|
233
|
+
private_key: str,
|
|
234
|
+
api_url: Optional[str] = None,
|
|
235
|
+
chain: str = "base",
|
|
236
|
+
) -> Dict[str, Any]:
|
|
237
|
+
"""
|
|
238
|
+
Check reputation score.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
agent_did: Optional DID to check (defaults to own reputation)
|
|
242
|
+
wallet_address: Wallet address for authentication
|
|
243
|
+
private_key: Private key for signing
|
|
244
|
+
api_url: Xache API URL
|
|
245
|
+
chain: Chain to use ('base' or 'solana')
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Dict with reputation info
|
|
249
|
+
"""
|
|
250
|
+
client = create_xache_client(wallet_address, private_key, api_url, chain)
|
|
251
|
+
|
|
252
|
+
async def _check():
|
|
253
|
+
async with client as c:
|
|
254
|
+
if agent_did:
|
|
255
|
+
result = await c.reputation.get_score(agent_did=agent_did)
|
|
256
|
+
else:
|
|
257
|
+
result = await c.reputation.get_score()
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
result = run_sync(_check())
|
|
261
|
+
|
|
262
|
+
score = result.get("score", 0)
|
|
263
|
+
|
|
264
|
+
# Determine level
|
|
265
|
+
if score >= 0.9:
|
|
266
|
+
level = "Elite"
|
|
267
|
+
elif score >= 0.7:
|
|
268
|
+
level = "Trusted"
|
|
269
|
+
elif score >= 0.5:
|
|
270
|
+
level = "Established"
|
|
271
|
+
elif score >= 0.3:
|
|
272
|
+
level = "Developing"
|
|
273
|
+
else:
|
|
274
|
+
level = "New"
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
"score": score,
|
|
278
|
+
"level": level,
|
|
279
|
+
"erc8004Enabled": bool(result.get("erc8004AgentId")),
|
|
280
|
+
"erc8004AgentId": result.get("erc8004AgentId"),
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# Function schemas for AutoGen
|
|
285
|
+
xache_functions = [
|
|
286
|
+
{
|
|
287
|
+
"name": "xache_memory_store",
|
|
288
|
+
"description": "Store a memory with cryptographic receipt. Use this for important information that should persist across sessions.",
|
|
289
|
+
"parameters": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"properties": {
|
|
292
|
+
"content": {
|
|
293
|
+
"type": "string",
|
|
294
|
+
"description": "The content to store"
|
|
295
|
+
},
|
|
296
|
+
"context": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"description": "Context/category for the memory (e.g., 'research', 'conversation')"
|
|
299
|
+
},
|
|
300
|
+
"tags": {
|
|
301
|
+
"type": "array",
|
|
302
|
+
"items": {"type": "string"},
|
|
303
|
+
"description": "Optional tags for categorization"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"required": ["content", "context"]
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"name": "xache_memory_retrieve",
|
|
311
|
+
"description": "Retrieve memories by semantic search. Use this to recall information from previous sessions.",
|
|
312
|
+
"parameters": {
|
|
313
|
+
"type": "object",
|
|
314
|
+
"properties": {
|
|
315
|
+
"query": {
|
|
316
|
+
"type": "string",
|
|
317
|
+
"description": "What to search for"
|
|
318
|
+
},
|
|
319
|
+
"context": {
|
|
320
|
+
"type": "string",
|
|
321
|
+
"description": "Optional context filter"
|
|
322
|
+
},
|
|
323
|
+
"limit": {
|
|
324
|
+
"type": "integer",
|
|
325
|
+
"description": "Maximum number of results (default: 5)"
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"required": ["query"]
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"name": "xache_collective_contribute",
|
|
333
|
+
"description": "Contribute an insight to the collective intelligence pool. Use this when you discover something valuable that could help other agents.",
|
|
334
|
+
"parameters": {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"properties": {
|
|
337
|
+
"insight": {
|
|
338
|
+
"type": "string",
|
|
339
|
+
"description": "The insight to contribute"
|
|
340
|
+
},
|
|
341
|
+
"domain": {
|
|
342
|
+
"type": "string",
|
|
343
|
+
"description": "Domain/topic of the insight"
|
|
344
|
+
},
|
|
345
|
+
"evidence": {
|
|
346
|
+
"type": "string",
|
|
347
|
+
"description": "Optional supporting evidence"
|
|
348
|
+
},
|
|
349
|
+
"tags": {
|
|
350
|
+
"type": "array",
|
|
351
|
+
"items": {"type": "string"},
|
|
352
|
+
"description": "Optional tags"
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"required": ["insight", "domain"]
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"name": "xache_collective_query",
|
|
360
|
+
"description": "Query the collective intelligence pool for insights from other agents.",
|
|
361
|
+
"parameters": {
|
|
362
|
+
"type": "object",
|
|
363
|
+
"properties": {
|
|
364
|
+
"query": {
|
|
365
|
+
"type": "string",
|
|
366
|
+
"description": "What to search for"
|
|
367
|
+
},
|
|
368
|
+
"domain": {
|
|
369
|
+
"type": "string",
|
|
370
|
+
"description": "Optional domain filter"
|
|
371
|
+
},
|
|
372
|
+
"limit": {
|
|
373
|
+
"type": "integer",
|
|
374
|
+
"description": "Maximum number of results (default: 5)"
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
"required": ["query"]
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"name": "xache_check_reputation",
|
|
382
|
+
"description": "Check reputation score and ERC-8004 status. Higher reputation means lower costs and more trust.",
|
|
383
|
+
"parameters": {
|
|
384
|
+
"type": "object",
|
|
385
|
+
"properties": {
|
|
386
|
+
"agent_did": {
|
|
387
|
+
"type": "string",
|
|
388
|
+
"description": "Optional DID to check (defaults to own reputation)"
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
]
|
xache_autogen/memory.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Xache Conversation Memory for AutoGen
|
|
3
|
+
Persistent storage for conversation history and context
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import uuid
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from xache import XacheClient
|
|
11
|
+
from ._async_utils import run_sync
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class XacheConversationMemory:
|
|
15
|
+
"""
|
|
16
|
+
Persistent conversation memory backed by Xache.
|
|
17
|
+
|
|
18
|
+
Stores conversation history with cryptographic receipts for
|
|
19
|
+
verification and persistence across sessions.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
```python
|
|
23
|
+
from xache_autogen import XacheConversationMemory
|
|
24
|
+
|
|
25
|
+
memory = XacheConversationMemory(
|
|
26
|
+
wallet_address="0x...",
|
|
27
|
+
private_key="0x...",
|
|
28
|
+
conversation_id="unique-id"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Store conversation turn
|
|
32
|
+
memory.add_message("user", "Hello!")
|
|
33
|
+
memory.add_message("assistant", "Hi there!")
|
|
34
|
+
|
|
35
|
+
# Retrieve history
|
|
36
|
+
history = memory.get_history()
|
|
37
|
+
|
|
38
|
+
# Summarize and store
|
|
39
|
+
memory.store_summary("User greeted, assistant responded.")
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
wallet_address: str,
|
|
46
|
+
private_key: str,
|
|
47
|
+
conversation_id: Optional[str] = None,
|
|
48
|
+
api_url: Optional[str] = None,
|
|
49
|
+
chain: str = "base",
|
|
50
|
+
):
|
|
51
|
+
self.wallet_address = wallet_address
|
|
52
|
+
self.api_url = api_url or os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
|
|
53
|
+
self.chain = chain
|
|
54
|
+
# Use UUID for collision-resistant conversation ID
|
|
55
|
+
self.conversation_id = conversation_id or f"autogen-{uuid.uuid4()}"
|
|
56
|
+
|
|
57
|
+
chain_prefix = "sol" if chain == "solana" else "evm"
|
|
58
|
+
self.did = f"did:agent:{chain_prefix}:{wallet_address.lower()}"
|
|
59
|
+
|
|
60
|
+
self._client = XacheClient(
|
|
61
|
+
api_url=self.api_url,
|
|
62
|
+
did=self.did,
|
|
63
|
+
private_key=private_key,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Local message buffer
|
|
67
|
+
self._messages: List[Dict[str, str]] = []
|
|
68
|
+
|
|
69
|
+
def add_message(self, role: str, content: str, metadata: Optional[Dict] = None):
|
|
70
|
+
"""
|
|
71
|
+
Add a message to the conversation.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
role: Message role (user, assistant, system)
|
|
75
|
+
content: Message content
|
|
76
|
+
metadata: Optional metadata
|
|
77
|
+
"""
|
|
78
|
+
import json
|
|
79
|
+
import time
|
|
80
|
+
|
|
81
|
+
message = {
|
|
82
|
+
"role": role,
|
|
83
|
+
"content": content,
|
|
84
|
+
"timestamp": time.time(),
|
|
85
|
+
}
|
|
86
|
+
self._messages.append(message)
|
|
87
|
+
|
|
88
|
+
# Store to Xache
|
|
89
|
+
async def _store():
|
|
90
|
+
async with self._client as client:
|
|
91
|
+
result = await client.memory.store(
|
|
92
|
+
content=json.dumps(message),
|
|
93
|
+
context=f"autogen:conversation:{self.conversation_id}",
|
|
94
|
+
tags=["autogen", "conversation", role],
|
|
95
|
+
metadata={
|
|
96
|
+
"conversationId": self.conversation_id,
|
|
97
|
+
"role": role,
|
|
98
|
+
**(metadata or {}),
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
run_sync(_store())
|
|
104
|
+
|
|
105
|
+
def get_history(self, limit: int = 50) -> List[Dict[str, str]]:
|
|
106
|
+
"""
|
|
107
|
+
Get conversation history from remote and update local cache.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
limit: Maximum messages to retrieve
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of messages
|
|
114
|
+
"""
|
|
115
|
+
import json
|
|
116
|
+
|
|
117
|
+
async def _retrieve():
|
|
118
|
+
async with self._client as client:
|
|
119
|
+
result = await client.memory.retrieve(
|
|
120
|
+
context=f"autogen:conversation:{self.conversation_id}",
|
|
121
|
+
limit=limit,
|
|
122
|
+
)
|
|
123
|
+
return result
|
|
124
|
+
|
|
125
|
+
result = run_sync(_retrieve())
|
|
126
|
+
|
|
127
|
+
# Defensive type checking
|
|
128
|
+
memories = result.get("memories", []) if isinstance(result, dict) else []
|
|
129
|
+
|
|
130
|
+
messages = []
|
|
131
|
+
for m in memories:
|
|
132
|
+
try:
|
|
133
|
+
msg = json.loads(m.get("content", "{}"))
|
|
134
|
+
messages.append(msg)
|
|
135
|
+
except json.JSONDecodeError:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
# Sort by timestamp
|
|
139
|
+
messages.sort(key=lambda x: x.get("timestamp", 0))
|
|
140
|
+
|
|
141
|
+
# Sync local cache with remote
|
|
142
|
+
self._messages = messages
|
|
143
|
+
|
|
144
|
+
return messages
|
|
145
|
+
|
|
146
|
+
def refresh(self) -> List[Dict[str, str]]:
|
|
147
|
+
"""
|
|
148
|
+
Refresh local cache from remote storage.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Updated list of messages
|
|
152
|
+
"""
|
|
153
|
+
return self.get_history()
|
|
154
|
+
|
|
155
|
+
def store_summary(self, summary: str, metadata: Optional[Dict] = None) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Store a conversation summary.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
summary: Summary text
|
|
161
|
+
metadata: Optional metadata
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Memory ID
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
async def _store():
|
|
168
|
+
async with self._client as client:
|
|
169
|
+
result = await client.memory.store(
|
|
170
|
+
content=summary,
|
|
171
|
+
context=f"autogen:summary:{self.conversation_id}",
|
|
172
|
+
tags=["autogen", "summary"],
|
|
173
|
+
metadata={
|
|
174
|
+
"conversationId": self.conversation_id,
|
|
175
|
+
"messageCount": len(self._messages),
|
|
176
|
+
**(metadata or {}),
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
return result
|
|
180
|
+
|
|
181
|
+
result = run_sync(_store())
|
|
182
|
+
|
|
183
|
+
return result.get("memoryId", "")
|
|
184
|
+
|
|
185
|
+
def search(self, query: str, limit: int = 5) -> List[Dict]:
|
|
186
|
+
"""
|
|
187
|
+
Search conversation history.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
query: Search query
|
|
191
|
+
limit: Maximum results
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
List of matching memories
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
async def _search():
|
|
198
|
+
async with self._client as client:
|
|
199
|
+
result = await client.memory.retrieve(
|
|
200
|
+
query=query,
|
|
201
|
+
context=f"autogen:conversation:{self.conversation_id}",
|
|
202
|
+
limit=limit,
|
|
203
|
+
)
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
result = run_sync(_search())
|
|
207
|
+
|
|
208
|
+
# Defensive type checking
|
|
209
|
+
if isinstance(result, dict):
|
|
210
|
+
return result.get("memories", [])
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
def clear_local(self):
|
|
214
|
+
"""Clear local message buffer (doesn't delete from Xache)"""
|
|
215
|
+
self._messages = []
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def messages(self) -> List[Dict[str, str]]:
|
|
219
|
+
"""Get local message buffer"""
|
|
220
|
+
return self._messages
|
|
221
|
+
|
|
222
|
+
def format_for_prompt(self, max_messages: int = 10) -> str:
|
|
223
|
+
"""
|
|
224
|
+
Format recent history for inclusion in prompt.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
max_messages: Maximum messages to include
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Formatted string
|
|
231
|
+
"""
|
|
232
|
+
recent = self._messages[-max_messages:] if self._messages else []
|
|
233
|
+
|
|
234
|
+
if not recent:
|
|
235
|
+
return ""
|
|
236
|
+
|
|
237
|
+
lines = []
|
|
238
|
+
for msg in recent:
|
|
239
|
+
role = msg.get("role", "unknown").capitalize()
|
|
240
|
+
content = msg.get("content", "")
|
|
241
|
+
lines.append(f"{role}: {content}")
|
|
242
|
+
|
|
243
|
+
return "\n".join(lines)
|