cronos3k commited on
Commit
26b7ba4
·
verified ·
1 Parent(s): 71583e6

v1.0.9: Add trust binding info to How It Works tab

Browse files
Files changed (1) hide show
  1. app.py +502 -489
app.py CHANGED
@@ -1,489 +1,502 @@
1
- """AgentAZAll HuggingFace Spaces Demo.
2
-
3
- A live demo of persistent memory for LLM agents, powered by SmolLM2-1.7B-Instruct
4
- on ZeroGPU and the AgentAZAll file-based memory system.
5
- """
6
-
7
- import sys
8
- from pathlib import Path
9
-
10
- # Ensure src/ is importable
11
- sys.path.insert(0, str(Path(__file__).parent / "src"))
12
-
13
- import gradio as gr
14
-
15
- from seed_data import (
16
- AGENTS,
17
- MAILBOXES,
18
- make_demo_config,
19
- reset_demo_data,
20
- seed_demo_data,
21
- )
22
- from llm_bridge import (
23
- _tool_directory,
24
- _tool_inbox,
25
- _tool_recall,
26
- _tool_whoami,
27
- _tool_doing,
28
- _tool_note,
29
- _tool_remember,
30
- _tool_send,
31
- chat_with_agent,
32
- )
33
- from agentazall.helpers import today_str
34
- from agentazall.config import INBOX, NOTES, REMEMBER, SENT
35
-
36
- # ---------------------------------------------------------------------------
37
- # Initialize
38
- # ---------------------------------------------------------------------------
39
-
40
- seed_demo_data()
41
- DEMO_CFG = make_demo_config("demo-agent@localhost")
42
-
43
- # ---------------------------------------------------------------------------
44
- # Chat tab functions
45
- # ---------------------------------------------------------------------------
46
-
47
-
48
- def agent_chat(message: str, history: list) -> str:
49
- """Chat with the demo agent."""
50
- if not message or not message.strip():
51
- return "Please type a message."
52
- try:
53
- return chat_with_agent(message.strip(), history, DEMO_CFG)
54
- except Exception as e:
55
- return f"Error: {e}\n\n(This may happen if GPU quota is exhausted. Try again later.)"
56
-
57
-
58
- def get_memory_sidebar() -> str:
59
- """Get current memory state for the sidebar."""
60
- return _tool_recall(DEMO_CFG, [])
61
-
62
-
63
- # ---------------------------------------------------------------------------
64
- # Dashboard tab functions
65
- # ---------------------------------------------------------------------------
66
-
67
-
68
- def get_directory() -> str:
69
- return _tool_directory(DEMO_CFG, [])
70
-
71
-
72
- def get_agent_memories(agent_name: str) -> str:
73
- if not agent_name:
74
- return "Select an agent."
75
- cfg = make_demo_config(agent_name)
76
- return _tool_recall(cfg, [])
77
-
78
-
79
- def get_agent_inbox(agent_name: str) -> str:
80
- if not agent_name:
81
- return "Select an agent."
82
- cfg = make_demo_config(agent_name)
83
- return _tool_inbox(cfg, [])
84
-
85
-
86
- def get_agent_identity(agent_name: str) -> str:
87
- if not agent_name:
88
- return "Select an agent."
89
- cfg = make_demo_config(agent_name)
90
- identity = _tool_whoami(cfg, [])
91
- doing = _tool_doing(cfg, [])
92
- return f"**Identity:** {identity}\n\n**Current task:** {doing}"
93
-
94
-
95
- def get_agent_notes(agent_name: str) -> str:
96
- if not agent_name:
97
- return "Select an agent."
98
- cfg = make_demo_config(agent_name)
99
- d = today_str()
100
- notes_dir = Path(cfg["mailbox_dir"]) / agent_name / d / NOTES
101
- if not notes_dir.exists():
102
- return "No notes."
103
- notes = []
104
- for f in sorted(notes_dir.iterdir()):
105
- if f.is_file() and f.suffix == ".txt":
106
- content = f.read_text(encoding="utf-8").strip()[:200]
107
- notes.append(f"**{f.stem}:** {content}")
108
- return "\n\n".join(notes) if notes else "No notes."
109
-
110
-
111
- def manual_remember(agent_name: str, text: str, title: str) -> str:
112
- if not agent_name or not text.strip():
113
- return "Need agent and text."
114
- cfg = make_demo_config(agent_name)
115
- args = [text.strip()]
116
- if title.strip():
117
- args.append(title.strip())
118
- return _tool_remember(cfg, args)
119
-
120
-
121
- def manual_send(from_agent: str, to_agent: str, subject: str, body: str) -> str:
122
- if not all([from_agent, to_agent, subject.strip(), body.strip()]):
123
- return "All fields required."
124
- cfg = make_demo_config(from_agent)
125
- return _tool_send(cfg, [to_agent, subject.strip(), body.strip()])
126
-
127
-
128
- def do_reset() -> str:
129
- return reset_demo_data()
130
-
131
-
132
- # ---------------------------------------------------------------------------
133
- # Agent name list for dropdowns
134
- # ---------------------------------------------------------------------------
135
-
136
- AGENT_NAMES = list(AGENTS.keys())
137
-
138
- # ---------------------------------------------------------------------------
139
- # Build Gradio UI
140
- # ---------------------------------------------------------------------------
141
-
142
- CSS = """
143
- .memory-sidebar { font-size: 0.85em; }
144
- .tool-result { background: #f0f4f8; padding: 8px; border-radius: 4px; margin: 4px 0; }
145
- footer { display: none !important; }
146
- """
147
-
148
- HOW_IT_WORKS_MD = """\
149
- ## How AgentAZAll Works
150
-
151
- AgentAZAll is a **persistent memory and multi-agent communication system** for LLM agents
152
- with **three interchangeable transport layers**. Pick the one that fits your setup —
153
- from the agent's perspective, they're all identical.
154
-
155
- ### Three Transports, One Interface
156
-
157
- | Transport | Protocol | Self-Host | Best For |
158
- |-----------|----------|-----------|----------|
159
- | **AgentTalk** | HTTPS REST API | `agentazall server --agenttalk` | Modern setups, zero config |
160
- | **Email** | SMTP + IMAP + POP3 | `agentazall server --email` | Universal compatibility |
161
- | **FTP** | FTP/FTPS | `agentazall server --ftp` | File-heavy workflows |
162
-
163
- All three are **open**, **self-hostable**, and **interchangeable**. Switch transports
164
- by changing one line in `config.json`.
165
-
166
- ### Free Public Relay
167
-
168
- Don't want to run your own server? Register on the free public relay in seconds:
169
-
170
- ```bash
171
- pip install agentazall
172
- agentazall register --agent myagent
173
- ```
174
-
175
- The relay uses **AgentTalk** -- a privacy-first HTTPS protocol:
176
- - **Zero-knowledge**: server relays opaque blobs, can't read messages
177
- - **RAM-only**: messages stored in volatile memory, erased on reboot
178
- - **Ephemeral**: messages auto-delete on retrieval, expire after 48h
179
-
180
- ### File-Based Storage
181
-
182
- Every agent gets a mailbox directory organized by date:
183
-
184
- ```
185
- data/mailboxes/
186
- demo-agent@localhost/
187
- 2026-03-08/
188
- inbox/ # received messages
189
- sent/ # delivered messages
190
- who_am_i/ # identity.txt
191
- what_am_i_doing/ # tasks.txt
192
- remember/ # persistent memories
193
- notes/ # working notes
194
- skills/ # reusable Python scripts
195
- tools/ # reusable tools
196
- ```
197
-
198
- ### Key Features
199
-
200
- | Feature | Commands | Description |
201
- |---------|----------|-------------|
202
- | **Persistent Memory** | `remember`, `recall` | Store and search memories that survive context resets |
203
- | **Inter-Agent Messaging** | `send`, `inbox`, `reply` | Agents communicate via any transport |
204
- | **Identity Continuity** | `whoami`, `doing` | Maintain identity and task state across sessions |
205
- | **Working Notes** | `note`, `notes` | Named notes for ongoing projects |
206
- | **Agent Directory** | `directory` | Discover other agents in the network |
207
- | **Skills & Tools** | `skill`, `tool` | Store and share reusable Python scripts |
208
-
209
- ### Integration with LLM Agents
210
-
211
- Add this to your agent's system prompt (e.g., `CLAUDE.md`):
212
-
213
- ```bash
214
- # At session start -- restore context:
215
- agentazall recall # what do I remember?
216
- agentazall whoami # who am I?
217
- agentazall doing # what was I doing?
218
- agentazall inbox # any new messages?
219
-
220
- # During work -- save important observations:
221
- agentazall remember --text "Important insight" --title "my-observation"
222
-
223
- # Before context runs low -- save state:
224
- agentazall doing --set "CURRENT: X. NEXT: Y."
225
- agentazall note handoff --set "detailed state for next session"
226
- ```
227
-
228
- ### Install & Run
229
-
230
- ```bash
231
- pip install agentazall
232
-
233
- # Quick start with public relay:
234
- agentazall register --agent myagent
235
-
236
- # Or self-host everything:
237
- agentazall setup --agent my-agent@localhost
238
- agentazall server --agenttalk # modern HTTPS API (port 8484)
239
- agentazall server --email # SMTP/IMAP/POP3 (ports 2525/1143/1110)
240
- agentazall server --ftp # FTP (port 2121)
241
- agentazall server --all # all three at once
242
- ```
243
-
244
- ### Architecture
245
-
246
- ```
247
- Agent <-> agentazall CLI <-> filesystem <-> Daemon <-> AgentTalk / Email / FTP servers
248
- Human <-> web_ui (Gradio) <-> agentazall CLI <-> filesystem
249
- ```
250
-
251
- - **Zero external dependencies** for core (Python stdlib only)
252
- - **File-based storage** -- no database, fully portable
253
- - **AgentTalk server** -- modern HTTPS REST API, self-host or use public relay
254
- - **Email server** (SMTP + IMAP + POP3) for universal compatibility
255
- - **FTP transport** -- the original internet file protocol, still everywhere
256
- - **Unlimited local** -- self-hosted AgentTalk has no file size or message limits
257
-
258
- ### Links
259
-
260
- - [GitHub Repository](https://github.com/cronos3k/AgentAZAll) -- source, issues, Rust fast relay
261
- - [PyPI Package](https://pypi.org/project/agentazall/) -- `pip install agentazall`
262
- - [This Live Demo](https://huggingface.co/spaces/cronos3k/AgentAZAll) -- chat with an agent on ZeroGPU
263
- - License: AGPL-3.0-or-later
264
- """
265
-
266
-
267
- def build_demo() -> gr.Blocks:
268
- """Build the complete Gradio demo interface."""
269
-
270
- with gr.Blocks(
271
- title="AgentAZAll - Persistent Memory for LLM Agents",
272
- theme=gr.themes.Soft(),
273
- css=CSS,
274
- ) as demo:
275
- gr.Markdown(
276
- "# AgentAZAll — Persistent Memory & Multi-Agent Communication\n"
277
- "Three transports (AgentTalk · Email · FTP), one interface. "
278
- "Chat with an agent that *remembers* — powered by "
279
- "[SmolLM2-1.7B](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct) "
280
- "on ZeroGPU."
281
- )
282
-
283
- # ==================================================================
284
- # Tab 1: Chat with Agent
285
- # ==================================================================
286
- with gr.Tab("Chat with Agent", id="chat"):
287
- with gr.Row():
288
- with gr.Column(scale=3):
289
- chatbot = gr.Chatbot(
290
- label="Demo Agent",
291
- height=480,
292
- type="messages",
293
- )
294
- msg_input = gr.Textbox(
295
- label="Your message",
296
- placeholder="Try: 'What do you remember?' or 'Remember that I love Python'",
297
- lines=2,
298
- )
299
- with gr.Row():
300
- send_btn = gr.Button("Send", variant="primary")
301
- clear_btn = gr.Button("Clear Chat")
302
-
303
- gr.Markdown("**Try these:**")
304
- examples = gr.Examples(
305
- examples=[
306
- "What do you remember about yourself?",
307
- "Please remember that my favorite language is Python.",
308
- "Check your inbox -- any new messages?",
309
- "Send a message to helper-agent@localhost saying hi!",
310
- "What agents are in the network?",
311
- "What are you currently working on?",
312
- "Recall anything about architecture.",
313
- ],
314
- inputs=msg_input,
315
- )
316
-
317
- with gr.Column(scale=1):
318
- gr.Markdown("### Agent Memory")
319
- memory_display = gr.Textbox(
320
- label="Current Memories",
321
- lines=18,
322
- interactive=False,
323
- elem_classes=["memory-sidebar"],
324
- )
325
- refresh_mem_btn = gr.Button("Refresh Memories", size="sm")
326
-
327
- # Chat event handling
328
- def respond(message, chat_history):
329
- if not message or not message.strip():
330
- return "", chat_history
331
- bot_response = agent_chat(message, chat_history)
332
- chat_history = chat_history + [
333
- {"role": "user", "content": message},
334
- {"role": "assistant", "content": bot_response},
335
- ]
336
- return "", chat_history
337
-
338
- send_btn.click(
339
- respond, [msg_input, chatbot], [msg_input, chatbot]
340
- )
341
- msg_input.submit(
342
- respond, [msg_input, chatbot], [msg_input, chatbot]
343
- )
344
- clear_btn.click(lambda: ([], ""), None, [chatbot, msg_input])
345
- refresh_mem_btn.click(get_memory_sidebar, [], memory_display)
346
-
347
- # Auto-load memories on tab open
348
- demo.load(get_memory_sidebar, [], memory_display)
349
-
350
- # ==================================================================
351
- # Tab 2: Agent Dashboard
352
- # ==================================================================
353
- with gr.Tab("Agent Dashboard", id="dashboard"):
354
- gr.Markdown("### Browse Agent State")
355
- gr.Markdown(
356
- "See the raw persistent data behind the agents. "
357
- "Everything here is stored as plain text files."
358
- )
359
-
360
- with gr.Row():
361
- with gr.Column(scale=1):
362
- agent_select = gr.Dropdown(
363
- choices=AGENT_NAMES,
364
- value=AGENT_NAMES[0],
365
- label="Select Agent",
366
- )
367
- dir_btn = gr.Button("Show Directory")
368
- dir_output = gr.Textbox(
369
- label="Agent Directory", lines=12, interactive=False
370
- )
371
- dir_btn.click(get_directory, [], dir_output)
372
-
373
- with gr.Column(scale=2):
374
- with gr.Tab("Identity"):
375
- id_output = gr.Markdown()
376
- id_btn = gr.Button("Load Identity")
377
- id_btn.click(get_agent_identity, [agent_select], id_output)
378
-
379
- with gr.Tab("Memories"):
380
- mem_output = gr.Textbox(
381
- label="Memories", lines=10, interactive=False
382
- )
383
- mem_btn = gr.Button("Load Memories")
384
- mem_btn.click(
385
- get_agent_memories, [agent_select], mem_output
386
- )
387
-
388
- with gr.Tab("Inbox"):
389
- inbox_output = gr.Textbox(
390
- label="Inbox", lines=8, interactive=False
391
- )
392
- inbox_btn = gr.Button("Load Inbox")
393
- inbox_btn.click(
394
- get_agent_inbox, [agent_select], inbox_output
395
- )
396
-
397
- with gr.Tab("Notes"):
398
- notes_output = gr.Markdown()
399
- notes_btn = gr.Button("Load Notes")
400
- notes_btn.click(
401
- get_agent_notes, [agent_select], notes_output
402
- )
403
-
404
- gr.Markdown("---")
405
- gr.Markdown("### Manual Operations")
406
-
407
- with gr.Row():
408
- with gr.Column():
409
- gr.Markdown("**Store a Memory**")
410
- man_agent = gr.Dropdown(
411
- choices=AGENT_NAMES, value=AGENT_NAMES[0],
412
- label="Agent",
413
- )
414
- man_text = gr.Textbox(label="Memory text", lines=2)
415
- man_title = gr.Textbox(
416
- label="Title (optional)", placeholder="auto-generated"
417
- )
418
- man_remember_btn = gr.Button("Remember")
419
- man_remember_out = gr.Textbox(
420
- label="Result", interactive=False
421
- )
422
- man_remember_btn.click(
423
- manual_remember,
424
- [man_agent, man_text, man_title],
425
- man_remember_out,
426
- )
427
-
428
- with gr.Column():
429
- gr.Markdown("**Send a Message**")
430
- send_from = gr.Dropdown(
431
- choices=AGENT_NAMES, value=AGENT_NAMES[2],
432
- label="From",
433
- )
434
- send_to = gr.Dropdown(
435
- choices=AGENT_NAMES, value=AGENT_NAMES[0],
436
- label="To",
437
- )
438
- send_subj = gr.Textbox(label="Subject")
439
- send_body = gr.Textbox(label="Body", lines=3)
440
- send_msg_btn = gr.Button("Send Message")
441
- send_msg_out = gr.Textbox(
442
- label="Result", interactive=False
443
- )
444
- send_msg_btn.click(
445
- manual_send,
446
- [send_from, send_to, send_subj, send_body],
447
- send_msg_out,
448
- )
449
-
450
- gr.Markdown("---")
451
- with gr.Row():
452
- reset_btn = gr.Button("Reset Demo Data", variant="stop")
453
- reset_out = gr.Textbox(label="Reset Status", interactive=False)
454
- reset_btn.click(do_reset, [], reset_out)
455
-
456
- # ==================================================================
457
- # Tab 3: How It Works
458
- # ==================================================================
459
- with gr.Tab("How It Works", id="docs"):
460
- gr.Markdown(HOW_IT_WORKS_MD)
461
-
462
- return demo
463
-
464
-
465
- # ---------------------------------------------------------------------------
466
- # Launch
467
- # ---------------------------------------------------------------------------
468
-
469
- def _find_free_port(start: int = 7860, end: int = 7960) -> int:
470
- """Find a free port in the given range."""
471
- import socket
472
- for port in range(start, end + 1):
473
- try:
474
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
475
- s.bind(("127.0.0.1", port))
476
- return port
477
- except OSError:
478
- continue
479
- return start # fallback
480
-
481
-
482
- if __name__ == "__main__":
483
- port = _find_free_port()
484
- demo = build_demo()
485
- demo.launch(
486
- server_name="0.0.0.0",
487
- server_port=port,
488
- share=False,
489
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """AgentAZAll HuggingFace Spaces Demo.
2
+
3
+ A live demo of persistent memory for LLM agents, powered by SmolLM2-1.7B-Instruct
4
+ on ZeroGPU and the AgentAZAll file-based memory system.
5
+ """
6
+
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ # Ensure src/ is importable
11
+ sys.path.insert(0, str(Path(__file__).parent / "src"))
12
+
13
+ import gradio as gr
14
+
15
+ from seed_data import (
16
+ AGENTS,
17
+ MAILBOXES,
18
+ make_demo_config,
19
+ reset_demo_data,
20
+ seed_demo_data,
21
+ )
22
+ from llm_bridge import (
23
+ _tool_directory,
24
+ _tool_inbox,
25
+ _tool_recall,
26
+ _tool_whoami,
27
+ _tool_doing,
28
+ _tool_note,
29
+ _tool_remember,
30
+ _tool_send,
31
+ chat_with_agent,
32
+ )
33
+ from agentazall.helpers import today_str
34
+ from agentazall.config import INBOX, NOTES, REMEMBER, SENT
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Initialize
38
+ # ---------------------------------------------------------------------------
39
+
40
+ seed_demo_data()
41
+ DEMO_CFG = make_demo_config("demo-agent@localhost")
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # Chat tab functions
45
+ # ---------------------------------------------------------------------------
46
+
47
+
48
+ def agent_chat(message: str, history: list) -> str:
49
+ """Chat with the demo agent."""
50
+ if not message or not message.strip():
51
+ return "Please type a message."
52
+ try:
53
+ return chat_with_agent(message.strip(), history, DEMO_CFG)
54
+ except Exception as e:
55
+ return f"Error: {e}\n\n(This may happen if GPU quota is exhausted. Try again later.)"
56
+
57
+
58
+ def get_memory_sidebar() -> str:
59
+ """Get current memory state for the sidebar."""
60
+ return _tool_recall(DEMO_CFG, [])
61
+
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # Dashboard tab functions
65
+ # ---------------------------------------------------------------------------
66
+
67
+
68
+ def get_directory() -> str:
69
+ return _tool_directory(DEMO_CFG, [])
70
+
71
+
72
+ def get_agent_memories(agent_name: str) -> str:
73
+ if not agent_name:
74
+ return "Select an agent."
75
+ cfg = make_demo_config(agent_name)
76
+ return _tool_recall(cfg, [])
77
+
78
+
79
+ def get_agent_inbox(agent_name: str) -> str:
80
+ if not agent_name:
81
+ return "Select an agent."
82
+ cfg = make_demo_config(agent_name)
83
+ return _tool_inbox(cfg, [])
84
+
85
+
86
+ def get_agent_identity(agent_name: str) -> str:
87
+ if not agent_name:
88
+ return "Select an agent."
89
+ cfg = make_demo_config(agent_name)
90
+ identity = _tool_whoami(cfg, [])
91
+ doing = _tool_doing(cfg, [])
92
+ return f"**Identity:** {identity}\n\n**Current task:** {doing}"
93
+
94
+
95
+ def get_agent_notes(agent_name: str) -> str:
96
+ if not agent_name:
97
+ return "Select an agent."
98
+ cfg = make_demo_config(agent_name)
99
+ d = today_str()
100
+ notes_dir = Path(cfg["mailbox_dir"]) / agent_name / d / NOTES
101
+ if not notes_dir.exists():
102
+ return "No notes."
103
+ notes = []
104
+ for f in sorted(notes_dir.iterdir()):
105
+ if f.is_file() and f.suffix == ".txt":
106
+ content = f.read_text(encoding="utf-8").strip()[:200]
107
+ notes.append(f"**{f.stem}:** {content}")
108
+ return "\n\n".join(notes) if notes else "No notes."
109
+
110
+
111
+ def manual_remember(agent_name: str, text: str, title: str) -> str:
112
+ if not agent_name or not text.strip():
113
+ return "Need agent and text."
114
+ cfg = make_demo_config(agent_name)
115
+ args = [text.strip()]
116
+ if title.strip():
117
+ args.append(title.strip())
118
+ return _tool_remember(cfg, args)
119
+
120
+
121
+ def manual_send(from_agent: str, to_agent: str, subject: str, body: str) -> str:
122
+ if not all([from_agent, to_agent, subject.strip(), body.strip()]):
123
+ return "All fields required."
124
+ cfg = make_demo_config(from_agent)
125
+ return _tool_send(cfg, [to_agent, subject.strip(), body.strip()])
126
+
127
+
128
+ def do_reset() -> str:
129
+ return reset_demo_data()
130
+
131
+
132
+ # ---------------------------------------------------------------------------
133
+ # Agent name list for dropdowns
134
+ # ---------------------------------------------------------------------------
135
+
136
+ AGENT_NAMES = list(AGENTS.keys())
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Build Gradio UI
140
+ # ---------------------------------------------------------------------------
141
+
142
+ CSS = """
143
+ .memory-sidebar { font-size: 0.85em; }
144
+ .tool-result { background: #f0f4f8; padding: 8px; border-radius: 4px; margin: 4px 0; }
145
+ footer { display: none !important; }
146
+ """
147
+
148
+ HOW_IT_WORKS_MD = """\
149
+ ## How AgentAZAll Works
150
+
151
+ AgentAZAll is a **persistent memory and multi-agent communication system** for LLM agents
152
+ with **three interchangeable transport layers**. Pick the one that fits your setup —
153
+ from the agent's perspective, they're all identical.
154
+
155
+ ### Three Transports, One Interface
156
+
157
+ | Transport | Protocol | Self-Host | Best For |
158
+ |-----------|----------|-----------|----------|
159
+ | **AgentTalk** | HTTPS REST API | `agentazall server --agenttalk` | Modern setups, zero config |
160
+ | **Email** | SMTP + IMAP + POP3 | `agentazall server --email` | Universal compatibility |
161
+ | **FTP** | FTP/FTPS | `agentazall server --ftp` | File-heavy workflows |
162
+
163
+ All three are **open**, **self-hostable**, and **interchangeable**. Switch transports
164
+ by changing one line in `config.json`.
165
+
166
+ ### Free Public Relay
167
+
168
+ Don't want to run your own server? Register on the free public relay in seconds:
169
+
170
+ ```bash
171
+ pip install agentazall
172
+ agentazall register --agent myagent
173
+ ```
174
+
175
+ The relay uses **AgentTalk** -- a privacy-first HTTPS protocol:
176
+ - **Zero-knowledge**: server relays opaque blobs, can't read messages
177
+ - **RAM-only**: messages stored in volatile memory, erased on reboot
178
+ - **Ephemeral**: messages auto-delete on retrieval, expire after 48h
179
+
180
+ ### File-Based Storage
181
+
182
+ Every agent gets a mailbox directory organized by date:
183
+
184
+ ```
185
+ data/mailboxes/
186
+ demo-agent@localhost/
187
+ 2026-03-08/
188
+ inbox/ # received messages
189
+ sent/ # delivered messages
190
+ who_am_i/ # identity.txt
191
+ what_am_i_doing/ # tasks.txt
192
+ remember/ # persistent memories
193
+ notes/ # working notes
194
+ skills/ # reusable Python scripts
195
+ tools/ # reusable tools
196
+ ```
197
+
198
+ ### Key Features
199
+
200
+ | Feature | Commands | Description |
201
+ |---------|----------|-------------|
202
+ | **Persistent Memory** | `remember`, `recall` | Store and search memories that survive context resets |
203
+ | **Inter-Agent Messaging** | `send`, `inbox`, `reply` | Agents communicate via any transport |
204
+ | **Identity Continuity** | `whoami`, `doing` | Maintain identity and task state across sessions |
205
+ | **Working Notes** | `note`, `notes` | Named notes for ongoing projects |
206
+ | **Agent Directory** | `directory` | Discover other agents in the network |
207
+ | **Skills & Tools** | `skill`, `tool` | Store and share reusable Python scripts |
208
+ | **Trust Binding** | `trust-gen`, `trust-bind`, `trust-status` | Cryptographic owner-agent binding (unjailbreakable) |
209
+
210
+ ### Trust Binding (v1.0.9)
211
+
212
+ Agents need to know who owns them -- and that relationship must be verified by
213
+ deterministic code, not LLM judgment. AgentAZAll uses **out-of-band trust tokens**:
214
+
215
+ 1. Run `agentazall trust-gen` on the machine where the agent lives (proves filesystem access)
216
+ 2. The token is HMAC-SHA256 signed, machine-fingerprinted, 4KB payload, 10-minute expiry
217
+ 3. Paste into the web UI or use `trust-bind --owner your-name` to bind
218
+ 4. Once bound, the agent rejects all other ownership claims -- only filesystem access can revoke
219
+
220
+ The verification runs in Python code. The LLM never sees or decides on trust tokens.
221
+
222
+ ### Integration with LLM Agents
223
+
224
+ Add this to your agent's system prompt or project instructions:
225
+
226
+ ```bash
227
+ # At session start -- restore context:
228
+ agentazall recall # what do I remember?
229
+ agentazall whoami # who am I?
230
+ agentazall doing # what was I doing?
231
+ agentazall inbox # any new messages?
232
+
233
+ # During work -- save important observations:
234
+ agentazall remember --text "Important insight" --title "my-observation"
235
+
236
+ # Before context runs low -- save state:
237
+ agentazall doing --set "CURRENT: X. NEXT: Y."
238
+ agentazall note handoff --set "detailed state for next session"
239
+ ```
240
+
241
+ ### Install & Run
242
+
243
+ ```bash
244
+ pip install agentazall
245
+
246
+ # Quick start with public relay:
247
+ agentazall register --agent myagent
248
+
249
+ # Or self-host everything:
250
+ agentazall setup --agent my-agent@localhost
251
+ agentazall server --agenttalk # modern HTTPS API (port 8484)
252
+ agentazall server --email # SMTP/IMAP/POP3 (ports 2525/1143/1110)
253
+ agentazall server --ftp # FTP (port 2121)
254
+ agentazall server --all # all three at once
255
+ ```
256
+
257
+ ### Architecture
258
+
259
+ ```
260
+ Agent <-> agentazall CLI <-> filesystem <-> Daemon <-> AgentTalk / Email / FTP servers
261
+ Human <-> web_ui (Gradio) <-> agentazall CLI <-> filesystem
262
+ ```
263
+
264
+ - **Zero external dependencies** for core (Python stdlib only)
265
+ - **File-based storage** -- no database, fully portable
266
+ - **AgentTalk server** -- modern HTTPS REST API, self-host or use public relay
267
+ - **Email server** (SMTP + IMAP + POP3) for universal compatibility
268
+ - **FTP transport** -- the original internet file protocol, still everywhere
269
+ - **Unlimited local** -- self-hosted AgentTalk has no file size or message limits
270
+
271
+ ### Links
272
+
273
+ - [GitHub Repository](https://github.com/cronos3k/AgentAZAll) -- source, issues, Rust fast relay
274
+ - [PyPI Package](https://pypi.org/project/agentazall/) -- `pip install agentazall`
275
+ - [This Live Demo](https://huggingface.co/spaces/cronos3k/AgentAZAll) -- chat with an agent on ZeroGPU
276
+ - License: AGPL-3.0-or-later
277
+ """
278
+
279
+
280
+ def build_demo() -> gr.Blocks:
281
+ """Build the complete Gradio demo interface."""
282
+
283
+ with gr.Blocks(
284
+ title="AgentAZAll - Persistent Memory for LLM Agents",
285
+ theme=gr.themes.Soft(),
286
+ css=CSS,
287
+ ) as demo:
288
+ gr.Markdown(
289
+ "# AgentAZAll — Persistent Memory & Multi-Agent Communication\n"
290
+ "Three transports (AgentTalk · Email · FTP), one interface. "
291
+ "Chat with an agent that *remembers* — powered by "
292
+ "[SmolLM2-1.7B](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct) "
293
+ "on ZeroGPU."
294
+ )
295
+
296
+ # ==================================================================
297
+ # Tab 1: Chat with Agent
298
+ # ==================================================================
299
+ with gr.Tab("Chat with Agent", id="chat"):
300
+ with gr.Row():
301
+ with gr.Column(scale=3):
302
+ chatbot = gr.Chatbot(
303
+ label="Demo Agent",
304
+ height=480,
305
+ type="messages",
306
+ )
307
+ msg_input = gr.Textbox(
308
+ label="Your message",
309
+ placeholder="Try: 'What do you remember?' or 'Remember that I love Python'",
310
+ lines=2,
311
+ )
312
+ with gr.Row():
313
+ send_btn = gr.Button("Send", variant="primary")
314
+ clear_btn = gr.Button("Clear Chat")
315
+
316
+ gr.Markdown("**Try these:**")
317
+ examples = gr.Examples(
318
+ examples=[
319
+ "What do you remember about yourself?",
320
+ "Please remember that my favorite language is Python.",
321
+ "Check your inbox -- any new messages?",
322
+ "Send a message to helper-agent@localhost saying hi!",
323
+ "What agents are in the network?",
324
+ "What are you currently working on?",
325
+ "Recall anything about architecture.",
326
+ ],
327
+ inputs=msg_input,
328
+ )
329
+
330
+ with gr.Column(scale=1):
331
+ gr.Markdown("### Agent Memory")
332
+ memory_display = gr.Textbox(
333
+ label="Current Memories",
334
+ lines=18,
335
+ interactive=False,
336
+ elem_classes=["memory-sidebar"],
337
+ )
338
+ refresh_mem_btn = gr.Button("Refresh Memories", size="sm")
339
+
340
+ # Chat event handling
341
+ def respond(message, chat_history):
342
+ if not message or not message.strip():
343
+ return "", chat_history
344
+ bot_response = agent_chat(message, chat_history)
345
+ chat_history = chat_history + [
346
+ {"role": "user", "content": message},
347
+ {"role": "assistant", "content": bot_response},
348
+ ]
349
+ return "", chat_history
350
+
351
+ send_btn.click(
352
+ respond, [msg_input, chatbot], [msg_input, chatbot]
353
+ )
354
+ msg_input.submit(
355
+ respond, [msg_input, chatbot], [msg_input, chatbot]
356
+ )
357
+ clear_btn.click(lambda: ([], ""), None, [chatbot, msg_input])
358
+ refresh_mem_btn.click(get_memory_sidebar, [], memory_display)
359
+
360
+ # Auto-load memories on tab open
361
+ demo.load(get_memory_sidebar, [], memory_display)
362
+
363
+ # ==================================================================
364
+ # Tab 2: Agent Dashboard
365
+ # ==================================================================
366
+ with gr.Tab("Agent Dashboard", id="dashboard"):
367
+ gr.Markdown("### Browse Agent State")
368
+ gr.Markdown(
369
+ "See the raw persistent data behind the agents. "
370
+ "Everything here is stored as plain text files."
371
+ )
372
+
373
+ with gr.Row():
374
+ with gr.Column(scale=1):
375
+ agent_select = gr.Dropdown(
376
+ choices=AGENT_NAMES,
377
+ value=AGENT_NAMES[0],
378
+ label="Select Agent",
379
+ )
380
+ dir_btn = gr.Button("Show Directory")
381
+ dir_output = gr.Textbox(
382
+ label="Agent Directory", lines=12, interactive=False
383
+ )
384
+ dir_btn.click(get_directory, [], dir_output)
385
+
386
+ with gr.Column(scale=2):
387
+ with gr.Tab("Identity"):
388
+ id_output = gr.Markdown()
389
+ id_btn = gr.Button("Load Identity")
390
+ id_btn.click(get_agent_identity, [agent_select], id_output)
391
+
392
+ with gr.Tab("Memories"):
393
+ mem_output = gr.Textbox(
394
+ label="Memories", lines=10, interactive=False
395
+ )
396
+ mem_btn = gr.Button("Load Memories")
397
+ mem_btn.click(
398
+ get_agent_memories, [agent_select], mem_output
399
+ )
400
+
401
+ with gr.Tab("Inbox"):
402
+ inbox_output = gr.Textbox(
403
+ label="Inbox", lines=8, interactive=False
404
+ )
405
+ inbox_btn = gr.Button("Load Inbox")
406
+ inbox_btn.click(
407
+ get_agent_inbox, [agent_select], inbox_output
408
+ )
409
+
410
+ with gr.Tab("Notes"):
411
+ notes_output = gr.Markdown()
412
+ notes_btn = gr.Button("Load Notes")
413
+ notes_btn.click(
414
+ get_agent_notes, [agent_select], notes_output
415
+ )
416
+
417
+ gr.Markdown("---")
418
+ gr.Markdown("### Manual Operations")
419
+
420
+ with gr.Row():
421
+ with gr.Column():
422
+ gr.Markdown("**Store a Memory**")
423
+ man_agent = gr.Dropdown(
424
+ choices=AGENT_NAMES, value=AGENT_NAMES[0],
425
+ label="Agent",
426
+ )
427
+ man_text = gr.Textbox(label="Memory text", lines=2)
428
+ man_title = gr.Textbox(
429
+ label="Title (optional)", placeholder="auto-generated"
430
+ )
431
+ man_remember_btn = gr.Button("Remember")
432
+ man_remember_out = gr.Textbox(
433
+ label="Result", interactive=False
434
+ )
435
+ man_remember_btn.click(
436
+ manual_remember,
437
+ [man_agent, man_text, man_title],
438
+ man_remember_out,
439
+ )
440
+
441
+ with gr.Column():
442
+ gr.Markdown("**Send a Message**")
443
+ send_from = gr.Dropdown(
444
+ choices=AGENT_NAMES, value=AGENT_NAMES[2],
445
+ label="From",
446
+ )
447
+ send_to = gr.Dropdown(
448
+ choices=AGENT_NAMES, value=AGENT_NAMES[0],
449
+ label="To",
450
+ )
451
+ send_subj = gr.Textbox(label="Subject")
452
+ send_body = gr.Textbox(label="Body", lines=3)
453
+ send_msg_btn = gr.Button("Send Message")
454
+ send_msg_out = gr.Textbox(
455
+ label="Result", interactive=False
456
+ )
457
+ send_msg_btn.click(
458
+ manual_send,
459
+ [send_from, send_to, send_subj, send_body],
460
+ send_msg_out,
461
+ )
462
+
463
+ gr.Markdown("---")
464
+ with gr.Row():
465
+ reset_btn = gr.Button("Reset Demo Data", variant="stop")
466
+ reset_out = gr.Textbox(label="Reset Status", interactive=False)
467
+ reset_btn.click(do_reset, [], reset_out)
468
+
469
+ # ==================================================================
470
+ # Tab 3: How It Works
471
+ # ==================================================================
472
+ with gr.Tab("How It Works", id="docs"):
473
+ gr.Markdown(HOW_IT_WORKS_MD)
474
+
475
+ return demo
476
+
477
+
478
+ # ---------------------------------------------------------------------------
479
+ # Launch
480
+ # ---------------------------------------------------------------------------
481
+
482
+ def _find_free_port(start: int = 7860, end: int = 7960) -> int:
483
+ """Find a free port in the given range."""
484
+ import socket
485
+ for port in range(start, end + 1):
486
+ try:
487
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
488
+ s.bind(("127.0.0.1", port))
489
+ return port
490
+ except OSError:
491
+ continue
492
+ return start # fallback
493
+
494
+
495
+ if __name__ == "__main__":
496
+ port = _find_free_port()
497
+ demo = build_demo()
498
+ demo.launch(
499
+ server_name="0.0.0.0",
500
+ server_port=port,
501
+ share=False,
502
+ )