reason-rdn 0.4.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.
rdn/__init__.py ADDED
@@ -0,0 +1,82 @@
1
+ """
2
+ rdn — The Coherent reason:// Substrate
3
+
4
+ One clean, local-first package that unifies:
5
+ - Persistent handoffs and project memory with a simple API
6
+ - Optional participation in the public reason:// Xchange network
7
+ - Protected structural fingerprints (local) + real scoring in the reference engine
8
+ - Clean resolution from the reason:// Xport registry
9
+
10
+ The simplest coherent API agents and humans should reach for:
11
+
12
+ import rdn as reason
13
+ reason.remember("Fixed the race with structural correlation", tags=["infra"])
14
+ art = reason.resolve("reason://ops/ecs/failures")
15
+
16
+ The advanced PCF mathematics and quality gating live in the protected reference engine.
17
+ This package is the open, IP-safe on-ramp to the reason:// ecosystem.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ __version__ = "0.4.0" # IP-protected coherent substrate
23
+
24
+ from .client import (
25
+ RDNClient,
26
+ XCHANGE_URL,
27
+ XCHANGE_BROKER_URL,
28
+ XPORT_URL,
29
+ REASON_XPORT_URL,
30
+ )
31
+ from .handoff.protocol import ReasonRDN
32
+ from .reason import (
33
+ Reason, # THE coherent high-level object
34
+ remember, # module-level simplest API
35
+ resolve,
36
+ list_prefix,
37
+ xchange_arbitrate,
38
+ status,
39
+ harness_metrics,
40
+ record_handoff,
41
+ record_recall,
42
+ ReasonClient,
43
+ WARFClient,
44
+ )
45
+ from .node.server import (
46
+ DEFAULT_HOST,
47
+ DEFAULT_PORT,
48
+ DEFAULT_STORAGE_DIR,
49
+ port_file_path,
50
+ default_db_path,
51
+ serve,
52
+ main as node_main,
53
+ )
54
+
55
+ __all__ = [
56
+ "RDNClient",
57
+ "ReasonRDN",
58
+ "Reason",
59
+ "remember",
60
+ "resolve",
61
+ "list_prefix",
62
+ "add_recent_uri",
63
+ "get_recent_uris",
64
+ "xchange_arbitrate",
65
+ "status",
66
+ "harness_metrics",
67
+ "record_handoff",
68
+ "record_recall",
69
+ "ReasonClient",
70
+ "WARFClient",
71
+ "XCHANGE_URL",
72
+ "XCHANGE_BROKER_URL",
73
+ "XPORT_URL",
74
+ "REASON_XPORT_URL",
75
+ "DEFAULT_HOST",
76
+ "DEFAULT_PORT",
77
+ "DEFAULT_STORAGE_DIR",
78
+ "port_file_path",
79
+ "default_db_path",
80
+ "serve",
81
+ "node_main",
82
+ ]
rdn/cli.py ADDED
@@ -0,0 +1,246 @@
1
+ """
2
+ rdn — friendly CLI for the ReasonRDN memory substrate.
3
+
4
+ Designed to be useful both for humans and for agents that can shell out.
5
+
6
+ Examples:
7
+ rdn remember "Fixed the critical race in the handoff protocol" --tags infra,bugfix
8
+ rdn recall "race condition" --project reason-ecosystem --limit 5
9
+ rdn resolve reason://reason-ecosystem/handoff/abc12345
10
+ rdn status
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import sys
18
+
19
+ from rdn.handoff import ReasonRDN
20
+ from rdn.client import RDNClient, XCHANGE_URL, XCHANGE_BROKER_URL, XPORT_URL, REASON_XPORT_URL
21
+ from rdn import __version__
22
+ import os # for env fallback for tokens
23
+
24
+
25
+ def _resolve_node_url(args) -> Optional[str]:
26
+ """Helper to pick node: explicit --node > --xchange (warf Xchange broker) > args.node"""
27
+ if args.xchange:
28
+ return XCHANGE_BROKER_URL # broker (warf.astrognosy.com) by default — scoring goes to astragnostic-api
29
+ return getattr(args, "node", None)
30
+
31
+
32
+ def cmd_remember(args):
33
+ node_url = _resolve_node_url(args)
34
+ rdn = ReasonRDN(node_url=node_url)
35
+ tags = args.tags.split(",") if args.tags else None
36
+ res = rdn.deposit_handoff(
37
+ project=args.project,
38
+ summary=args.content,
39
+ state_tokens=(args.tokens or "").split() if args.tokens else [args.content[:30]],
40
+ tags=tags,
41
+ )
42
+ # Real token accounting: use public record helper (avoids double-deposit while ensuring
43
+ # HarnessMetrics gets the tokens_used for accurate savings/velocity/ship-rate).
44
+ tokens_used = getattr(args, "tokens_used", None) or os.environ.get("RDN_TOKENS_USED")
45
+ if tokens_used:
46
+ try:
47
+ import rdn as reason
48
+ reason.record_handoff(args.content, tags, tokens_used=int(tokens_used))
49
+ except Exception:
50
+ pass
51
+ print(json.dumps(res, indent=2))
52
+
53
+
54
+ def cmd_recall(args):
55
+ node_url = _resolve_node_url(args)
56
+ client = RDNClient(node_url=node_url)
57
+ results = client.recall(
58
+ query=args.query,
59
+ project=args.project,
60
+ limit=args.limit,
61
+ )
62
+ # Real token accounting for recalls (savings when an agent uses prior high-quality artifact).
63
+ tokens_saved = getattr(args, "tokens_saved", None) or os.environ.get("RDN_TOKENS_SAVED")
64
+ if tokens_saved:
65
+ try:
66
+ import rdn as reason
67
+ reason.record_recall(args.query, tokens_saved=int(tokens_saved))
68
+ except Exception:
69
+ pass
70
+ print(json.dumps({"results": results}, indent=2))
71
+
72
+
73
+ def cmd_resolve(args):
74
+ node_url = _resolve_node_url(args)
75
+ client = RDNClient(node_url=node_url)
76
+ art = client.resolve(args.address)
77
+ if art:
78
+ print(json.dumps(art, indent=2))
79
+ else:
80
+ print(json.dumps({"status": "not_found", "address": args.address}, indent=2))
81
+ sys.exit(1)
82
+
83
+ # Real token accounting for resolves (savings from using the current canonical from Xport).
84
+ tokens_saved = getattr(args, "tokens_saved", None) or os.environ.get("RDN_TOKENS_SAVED")
85
+ if tokens_saved:
86
+ try:
87
+ import rdn as reason
88
+ reason.record_recall(args.address, tokens_saved=int(tokens_saved))
89
+ except Exception:
90
+ pass
91
+
92
+
93
+ def cmd_status(args):
94
+ node_url = _resolve_node_url(args)
95
+ client = RDNClient(node_url=node_url)
96
+ print("ReasonRDN status")
97
+ print(" Node URL :", client.node_url or "local fallback only")
98
+ print(" Broker :", getattr(client, "broker_url", None) or "same as node")
99
+ print(" Xport :", getattr(client, "xport_url", None) or XPORT_URL, "(reason.astrognosy.com)")
100
+ print(" Available:", client.available)
101
+ print(" Local DB :", client.db_path)
102
+ projects = client.get_recent_projects(8)
103
+ print(" Recent projects:", ", ".join(projects) if projects else "(none yet)")
104
+ hb = client.get_heartbeat()
105
+ print(" 7-day heartbeat:", hb)
106
+ try:
107
+ import rdn as reason
108
+ recent = reason.get_recent_uris()
109
+ print(" Recent URIs:", ", ".join(recent[:5]) if recent else "(none yet)")
110
+ s = reason.status()
111
+ top = s.get("top_prefixes", [])
112
+ if top:
113
+ print(" Top prefixes:", ", ".join(top))
114
+ except Exception:
115
+ pass
116
+
117
+
118
+ def cmd_xchange_arbitrate(args):
119
+ node_url = _resolve_node_url(args)
120
+ client = RDNClient(node_url=node_url)
121
+
122
+ packages = []
123
+ for p in args.package:
124
+ if ":" not in p:
125
+ print(f"Bad package format (need agent_id:answer): {p}")
126
+ return
127
+ aid, ans = p.split(":", 1)
128
+ packages.append({"agent_id": aid.strip(), "answer_text": ans.strip()})
129
+
130
+ result = client.xchange_arbitrate(
131
+ query_text=args.query,
132
+ packages=packages,
133
+ reason_address=args.uri
134
+ )
135
+ print(json.dumps(result, indent=2))
136
+
137
+
138
+ def cmd_list(args):
139
+ node_url = _resolve_node_url(args)
140
+ client = RDNClient(node_url=node_url)
141
+ results = client.list_prefix(args.prefix, limit=args.limit)
142
+ print(json.dumps({"results": results}, indent=2))
143
+
144
+
145
+ def main():
146
+ parser = argparse.ArgumentParser(
147
+ prog="rdn",
148
+ description="ReasonRDN memory substrate CLI. Easy for humans, perfect for agents. Supports auto-deposit to warf Xchange."
149
+ )
150
+ parser.add_argument(
151
+ "--version",
152
+ action="version",
153
+ version=f"rdn {__version__}",
154
+ )
155
+ parser.add_argument("--node", default=None, help="Override node URL (default: auto-discover local private node)")
156
+ parser.add_argument(
157
+ "--xchange", "--use-xchange", action="store_true",
158
+ help="Use the warf Xchange broker (warf.astrognosy.com) for deposits/arbitration "
159
+ "(scoring goes to astragnostic-api). Resolution uses Xport at reason.astrognosy.com. "
160
+ "Equivalent to REASON_USE_XCHANGE=1. Great for ecosystem sharing."
161
+ )
162
+
163
+ sub = parser.add_subparsers(dest="cmd", required=True)
164
+
165
+ p_rem = sub.add_parser("remember", help="Deposit a handoff / memory")
166
+ p_rem.add_argument("content", help="The summary or content to remember")
167
+ p_rem.add_argument("--project", default="astrognosy")
168
+ p_rem.add_argument("--tags", default=None, help="Comma-separated tags")
169
+ p_rem.add_argument("--tokens", default=None, help="Extra tokens for structural fingerprint (space separated)")
170
+ p_rem.add_argument("--tokens-used", type=int, default=None, dest="tokens_used",
171
+ help="Tokens consumed to produce this handoff (for accurate harness metrics / token savings tracking)")
172
+ p_rem.set_defaults(func=cmd_remember)
173
+
174
+ p_rec = sub.add_parser("recall", help="Search memory")
175
+ p_rec.add_argument("query", help="Search query")
176
+ p_rec.add_argument("--project", default=None)
177
+ p_rec.add_argument("--limit", type=int, default=10)
178
+ p_rec.add_argument("--tokens-saved", type=int, default=None, dest="tokens_saved",
179
+ help="Tokens saved by using this recall (for accurate harness metrics)")
180
+ p_rec.set_defaults(func=cmd_recall)
181
+
182
+ p_res = sub.add_parser("resolve", help="Fetch exact artifact by reason:// address")
183
+ p_res.add_argument("address")
184
+ p_res.add_argument("--tokens-saved", type=int, default=None, dest="tokens_saved",
185
+ help="Tokens saved by resolving and using this artifact (for accurate harness metrics)")
186
+ p_res.set_defaults(func=cmd_resolve)
187
+
188
+ p_stat = sub.add_parser("status", help="Show current node/client status + heartbeat")
189
+ p_stat.set_defaults(func=cmd_status)
190
+
191
+ # Xchange-specific convenience subcommands (full broker flow)
192
+ p_xarb = sub.add_parser("xchange-arbitrate", help="Submit packages for full Xchange arbitration (hits broker -> astragnostic-api)")
193
+ p_xarb.add_argument("query", help="The query / problem being arbitrated")
194
+ p_xarb.add_argument("--package", action="append", required=True,
195
+ help="agent_id:answer_text (repeatable). Example: --package agent1:the answer")
196
+ p_xarb.add_argument("--uri", help="Optional reason:// URI for promotion target")
197
+ p_xarb.set_defaults(func=cmd_xchange_arbitrate)
198
+
199
+ # List / browse prefix (new for namespace exploration)
200
+ p_list = sub.add_parser("list", help="List artifacts under a reason:// prefix (browse the namespace with partial URIs)")
201
+ p_list.add_argument("prefix", help="e.g. reason://grok or grok/build or warf")
202
+ p_list.add_argument("--limit", type=int, default=20, help="Max results to show")
203
+ p_list.set_defaults(func=cmd_list)
204
+
205
+ # The magical one-liner experience
206
+ p_start = sub.add_parser("start", help="The easiest way to start using rdn. Launches the badass agnostic harness dashboard with full excitement and metrics.")
207
+ p_start.set_defaults(func=lambda args: launch_harness())
208
+
209
+ args = parser.parse_args()
210
+ if hasattr(args, "cmd") and args.cmd == "start":
211
+ launch_harness()
212
+ else:
213
+ args.func(args)
214
+
215
+
216
+ def launch_harness():
217
+ """The magical one-liner 'start using rdn' experience."""
218
+ print("\n" + "="*70)
219
+ print("🚀 START USING RDN — THE AGNOSTIC HARNESS IS NOW LIVE")
220
+ print("="*70)
221
+ print("""
222
+ Instant features you just gained (first-run tour — what you unlocked):
223
+ • Persistent local mirror (private node at 8765 + SQLite) that works offline and survives agent sessions
224
+ • Real token accounting: report --tokens-used when depositing; get accurate savings when you (or agents) resolve prior external winners
225
+ • Easy bridge to the external WARF network flywheel: set REASON_USE_XCHANGE=1 or use --xchange; supply a reason:// URI and a strong artifact can compete under Xtend (in astragnostic-api) to become the new canonical for that URI on the public Xport (reason.astrognosy.com)
226
+ • IP-safe by design: local structural fingerprints are the hardcoded protected output from _pcf.py; the real PCF + Xtend scoring and promotion live in the external engine
227
+ • Live harness metrics (token savings from external promoted artifacts, velocity, ship rate, vibe stars) + flywheel-aware suggestions that nudge you to produce high-signal deposits worth promoting
228
+ • Unified `import rdn as reason` + CLI/MCP/dash that all feed the same coherent surface
229
+
230
+ This is the production-grade on-ramp to the full Astrognosy WARF / reason:// stack. Local-first, ecosystem-native, ridiculously useful.
231
+
232
+ Launching the full visual harness now...
233
+ """)
234
+ try:
235
+ import subprocess, sys
236
+ # Proper launch for the Streamlit dashboard (the visual harness)
237
+ subprocess.run([sys.executable, "-m", "streamlit", "run", "rdn/dash.py"], check=False)
238
+ except Exception:
239
+ print("\nTo launch the badass dashboard:")
240
+ print(" pip install 'reason-rdn[dash]'")
241
+ print(" rdn start")
242
+ print(" # or: python -m rdn.dash")
243
+
244
+
245
+ if __name__ == "__main__":
246
+ main()