Spaces:
Running
Running
chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)
Browse filesAutomated backend sync from szl-holdings/a11oy main via hf-sync-backend.
Updated (differed from the Space): a11oy_ayllu.py, ayllu/__init__.py, ayllu/lounge.py, ayllu/personas.py
Deleted (gone from the repo + Dockerfile COPY set): (none)
Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.
- a11oy_ayllu.py +158 -41
- ayllu/__init__.py +1 -1
- ayllu/lounge.py +47 -2
- ayllu/personas.py +11 -3
a11oy_ayllu.py
CHANGED
|
@@ -68,6 +68,7 @@ __version__ = _AYLLU_VERSION
|
|
| 68 |
# ---- cost + abuse bounds (public Space; real token cost once live) -----------
|
| 69 |
MAX_PROMPT_CHARS = 6000
|
| 70 |
COUNCIL_MAX = 5 # hard cap on participants / call
|
|
|
|
| 71 |
COUNCIL_DEFAULT = ["Amaru", "Kamachiq", "Qhatuq"] # architect · orchestrator · markets
|
| 72 |
|
| 73 |
# One process-wide lounge (in-memory, honest source labels).
|
|
@@ -134,11 +135,14 @@ _PAGE = r"""<!doctype html><html lang="en"><head><meta charset="utf-8">
|
|
| 134 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 135 |
<title>Ayllu — a11oy agent council</title>
|
| 136 |
<style>
|
| 137 |
-
:root{--void:#080c14;--panel:#0d1520;--line:#16202c;--teal:#3af4c8;--fg:#dfe7ee;--dim:#7f93a6}
|
| 138 |
*{box-sizing:border-box}
|
| 139 |
body{margin:0;background:var(--void);color:var(--fg);
|
| 140 |
font:15px/1.55 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
| 142 |
h1{color:var(--teal);margin:0 0 4px;font-size:26px;display:flex;align-items:center;gap:10px}
|
| 143 |
h2{font-size:16px;margin:0 0 10px;color:var(--fg)}
|
| 144 |
.sub{color:var(--dim);margin:0 0 22px}
|
|
@@ -160,14 +164,19 @@ button.mini{background:transparent;color:var(--teal);border:1px solid var(--line
|
|
| 160 |
font-weight:600;font-size:12px}
|
| 161 |
.hint{color:var(--dim);font-size:12px;margin:0 0 8px}
|
| 162 |
.out{margin-top:10px}
|
| 163 |
-
.turn{border
|
| 164 |
-
.turnh{display:flex;
|
| 165 |
-
.
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
| 167 |
.rcpt{color:var(--dim);font-size:12px;margin-top:8px}
|
| 168 |
.note{color:#da3;font-size:12px;margin-bottom:8px}
|
| 169 |
.err{color:#e66}
|
| 170 |
.stub{color:#da3;font-weight:700;font-size:11px}
|
|
|
|
|
|
|
| 171 |
table{width:100%;border-collapse:collapse}
|
| 172 |
th,td{text-align:left;padding:7px 9px;border-bottom:1px solid var(--line);font-size:14px}
|
| 173 |
th{color:var(--teal);font-weight:600}
|
|
@@ -178,22 +187,34 @@ padding:12px 14px;border-radius:6px;color:var(--dim);margin-top:6px;font-size:13
|
|
| 178 |
code{color:var(--teal)}
|
| 179 |
html{scroll-behavior:smooth}
|
| 180 |
.topbar{position:sticky;top:0;z-index:20;background:rgba(8,12,20,.92);backdrop-filter:blur(6px);border-bottom:1px solid var(--line)}
|
| 181 |
-
.tb-wrap{max-width:
|
| 182 |
.tb-brand{color:var(--teal);font-weight:800;text-decoration:none;font-size:18px;display:flex;align-items:center;gap:8px}
|
| 183 |
.tb-nav{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
| 184 |
.tb-nav a{color:var(--dim);text-decoration:none;font-size:13px}
|
| 185 |
.tb-nav a:hover{color:var(--fg)}
|
| 186 |
-
.tb-nav a.tb-home{color:
|
| 187 |
section[id]{scroll-margin-top:72px}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
</style></head><body>
|
| 189 |
<header class="topbar"><div class="tb-wrap">
|
| 190 |
<a class="tb-brand" href="/ayllu">Ayllu <span id="badge" class="badge">…</span></a>
|
| 191 |
-
<nav class="tb-nav"><a href="#sec-ask">Ask</a><a href="#sec-council">Council</a><a href="#sec-roster">Roster</a><a href="#sec-lounge">Lounge</a><a href="#sec-mesh">Mesh</a><a class="tb-home" href="/console" title="Back to the a11oy command centre">← a11oy command centre</a></nav>
|
| 192 |
</div></header>
|
| 193 |
<main>
|
| 194 |
<h1>Ayllu</h1>
|
| 195 |
<p class="sub">The AlloyScape tribe, ingested and reborn as a11oy's own agent community —
|
| 196 |
-
<span id="count">?</span> personas, one guarded loop. v__VERSION__
|
|
|
|
| 197 |
|
| 198 |
<section class="card" id="sec-ask">
|
| 199 |
<h2>Ask a persona</h2>
|
|
@@ -210,8 +231,11 @@ section[id]{scroll-margin-top:72px}
|
|
| 210 |
<section class="card" id="sec-council">
|
| 211 |
<h2>Convene a council</h2>
|
| 212 |
<p class="hint">Defaults to 3 core personas; select up to 5 (⌘/Ctrl-click). Fan-out is
|
| 213 |
-
capped to protect cost.
|
|
|
|
| 214 |
<select id="councilsel" multiple size="6"></select>
|
|
|
|
|
|
|
| 215 |
<textarea id="councilprompt" rows="3" placeholder="A question for the council…"></textarea>
|
| 216 |
<button id="councilbtn">Convene</button>
|
| 217 |
<div id="councilout" class="out"></div>
|
|
@@ -220,7 +244,10 @@ section[id]{scroll-margin-top:72px}
|
|
| 220 |
<section class="card" id="sec-roster">
|
| 221 |
<h2>Roster</h2>
|
| 222 |
<table id="roster"><thead><tr><th>Persona</th><th>Quechua</th><th>Archetype</th>
|
| 223 |
-
<th>a11oy domain</th><th>Autonomy</th></tr></thead><tbody></tbody></table>
|
|
|
|
|
|
|
|
|
|
| 224 |
</section>
|
| 225 |
|
| 226 |
<section class="card" id="sec-lounge">
|
|
@@ -228,6 +255,25 @@ section[id]{scroll-margin-top:72px}
|
|
| 228 |
<div id="lounge" class="out"></div>
|
| 229 |
</section>
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
<section class="card" id="sec-mesh">
|
| 232 |
<h2>Mesh & observability <span class="stub" id="mesh-badge">…</span></h2>
|
| 233 |
<div class="src" style="margin:.2rem 0 .6rem">Live platform context the council runs
|
|
@@ -242,21 +288,36 @@ fail-closed Λ-gate; state-changing actions require two-person attestation. The
|
|
| 242 |
"always execute" mandate is deliberately <b>not</b> adopted. Answers come from a11oy's
|
| 243 |
own model backend + router; when no inference credential is set on this Space,
|
| 244 |
<code>ask</code>/<code>council</code> return a clearly-labeled stub — never a fabricated
|
| 245 |
-
answer.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
</main>
|
| 247 |
<script>
|
| 248 |
const NS="__NS__";
|
| 249 |
const api = p => `/api/${NS}/v1/ayllu/`+p;
|
|
|
|
| 250 |
async function j(url,opts){const r=await fetch(url,opts);
|
| 251 |
let d={};try{d=await r.json();}catch(e){}return {ok:r.ok,status:r.status,data:d};}
|
| 252 |
function esc(s){return (s==null?'':String(s)).replace(/[&<>]/g,
|
| 253 |
c=>({'&':'&','<':'<','>':'>'}[c]));}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
function renderTurn(t){
|
| 255 |
const ans = t.answer!=null ? esc(t.answer) : '<i>'+esc(t.honesty)+'</i>';
|
| 256 |
const model = t.model?('model: '+esc(t.model)):'no model';
|
| 257 |
const tier = (t.tier&&t.tier.route)?(' · tier(advisory): '+esc(t.tier.route)):'';
|
| 258 |
const stub = t.stub?' · <span class="stub">STUB</span>':'';
|
| 259 |
-
|
|
|
|
|
|
|
| 260 |
+ `<span class="meta">${model}${tier}${stub}</span></div>`
|
| 261 |
+ `<div class="ans">${ans}</div></div>`;
|
| 262 |
}
|
|
@@ -274,9 +335,10 @@ async function loadRoster(){
|
|
| 274 |
const o=document.createElement('option');o.value=p.name;
|
| 275 |
o.textContent=p.name+' — '+p.domain;sel.appendChild(o);
|
| 276 |
csel.appendChild(o.cloneNode(true));
|
| 277 |
-
|
|
|
|
| 278 |
+`<td>${esc(p.archetype)}</td><td>${esc(p.domain)}</td>`
|
| 279 |
-
+`<td>${esc(p.autonomy_level)}</td></tr>`);
|
| 280 |
});
|
| 281 |
document.querySelector('#roster tbody').innerHTML=rows.join('');
|
| 282 |
}
|
|
@@ -301,16 +363,27 @@ document.getElementById('councilbtn').onclick=async()=>{
|
|
| 301 |
const out=document.getElementById('councilout');
|
| 302 |
if(!prompt){out.innerHTML='<span class="err">enter a prompt</span>';return;}
|
| 303 |
const picks=[...document.getElementById('councilsel').selectedOptions].map(o=>o.value);
|
| 304 |
-
|
| 305 |
-
|
|
|
|
| 306 |
const {ok,status,data}=await j(api('council'),{method:'POST',
|
| 307 |
headers:{'content-type':'application/json'},body:JSON.stringify(body)});
|
| 308 |
if(!ok){out.innerHTML='<span class="err">'+esc(data.error||('HTTP '+status))+'</span>'
|
| 309 |
+(data.retry_after_s?(' (retry in '+data.retry_after_s+'s)'):'');return;}
|
| 310 |
-
const
|
| 311 |
-
const cap=(
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
};
|
| 315 |
async function loadLounge(){
|
| 316 |
const {ok,data}=await j(api('lounge')), out=document.getElementById('lounge');
|
|
@@ -321,26 +394,64 @@ async function loadLounge(){
|
|
| 321 |
:'<i>empty</i>';
|
| 322 |
}
|
| 323 |
document.getElementById('refreshlounge').onclick=loadLounge;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
async function loadMesh(){
|
| 325 |
const badge=document.getElementById('mesh-badge'), out=document.getElementById('mesh-out');
|
| 326 |
-
const {ok,status,data}=await j('
|
| 327 |
-
if(!ok){badge.textContent='offline';
|
| 328 |
-
out.innerHTML='<span class="src">mesh state endpoint unavailable ('+esc(String(status))+') — shown honestly, not faked.</span>';return;}
|
| 329 |
-
const d=(data&&data.data&&typeof data.data==='object')?data.data:data;
|
| 330 |
-
const nodes=d.nodes||d.mesh||d.members||d.organs||[];
|
| 331 |
-
const cnt=(d.count!=null)?d.count:(Array.isArray(nodes)?nodes.length:'\u2014');
|
| 332 |
badge.textContent='live';
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
}
|
| 335 |
async function loadObs(){
|
| 336 |
const out=document.getElementById('obs-out');
|
| 337 |
-
const {ok,status,data}=await j('
|
| 338 |
-
if(!ok){
|
| 339 |
-
const d=(data&&data.data&&typeof data.data==='object')?data.data:data;
|
| 340 |
out.innerHTML='<div class="lg"><b>observability</b><pre class="src" style="white-space:pre-wrap;margin:.3rem 0 0">'
|
| 341 |
-
+esc(JSON.stringify(
|
| 342 |
}
|
| 343 |
-
loadRoster();loadLounge();loadMesh();loadObs();
|
| 344 |
</script>
|
| 345 |
</body></html>"""
|
| 346 |
|
|
@@ -439,18 +550,21 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 439 |
return JSONResponse(
|
| 440 |
{"error": f"'prompt' must be a string ≤ {MAX_PROMPT_CHARS} chars",
|
| 441 |
"max_chars": MAX_PROMPT_CHARS}, status_code=422)
|
|
|
|
|
|
|
| 442 |
requested = body.get("personas")
|
| 443 |
cap_note = None
|
| 444 |
if requested:
|
| 445 |
if not isinstance(requested, list):
|
| 446 |
return JSONResponse({"error": "'personas' must be a list"},
|
| 447 |
status_code=422)
|
| 448 |
-
names = [str(n) for n in requested][:
|
| 449 |
-
if len(requested) >
|
| 450 |
cap_note = (f"requested {len(requested)} personas; capped to "
|
| 451 |
-
f"{
|
|
|
|
| 452 |
else:
|
| 453 |
-
names = list(COUNCIL_DEFAULT)
|
| 454 |
cap_note = (f"no personas specified; convened the {len(names)} core "
|
| 455 |
"personas (Amaru, Kamachiq, Qhatuq)")
|
| 456 |
personas = [get_persona(n) for n in names]
|
|
@@ -460,7 +574,8 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 460 |
{"error": "no known personas in request",
|
| 461 |
"known": [x.name for x in ROSTER]}, status_code=422)
|
| 462 |
result = await _LOUNGE.deliberate(prompt, personas,
|
| 463 |
-
model_complete=_backend.model_complete
|
|
|
|
| 464 |
if cap_note:
|
| 465 |
result["cap_note"] = cap_note
|
| 466 |
council_id = str(uuid.uuid4())
|
|
@@ -468,6 +583,7 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 468 |
"council_id": council_id,
|
| 469 |
"prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
|
| 470 |
"participants": result["participants"],
|
|
|
|
| 471 |
"models": [r.get("model") for r in result.get("rounds", [])],
|
| 472 |
})
|
| 473 |
return JSONResponse({"council_id": council_id, "result": result,
|
|
@@ -488,7 +604,7 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 488 |
summary="Ask one persona — bounded, honest, receipted")
|
| 489 |
app.add_api_route(f"/api/{ns}/v1/ayllu/council", _council, methods=["POST"],
|
| 490 |
tags=["ayllu"],
|
| 491 |
-
summary="Bounded multi-persona deliberation (capped fan-out)")
|
| 492 |
app.add_api_route(f"/api/{ns}/v1/ayllu/lounge", _lounge_feed, methods=["GET"],
|
| 493 |
tags=["ayllu"], summary="Recent collaboration lounge feed")
|
| 494 |
app.add_api_route("/ayllu", _page, methods=["GET"], include_in_schema=False)
|
|
@@ -496,5 +612,6 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 496 |
return (
|
| 497 |
f"ok — ayllu registered: {len(ROSTER)} personas; live model backend "
|
| 498 |
f"({_backend.backend_status().get('mode')}); bounded-autonomy Λ-gate; "
|
| 499 |
-
f"/ayllu + /api/{ns}/v1/ayllu/roster|ask|council|lounge;
|
|
|
|
| 500 |
)
|
|
|
|
| 68 |
# ---- cost + abuse bounds (public Space; real token cost once live) -----------
|
| 69 |
MAX_PROMPT_CHARS = 6000
|
| 70 |
COUNCIL_MAX = 5 # hard cap on participants / call
|
| 71 |
+
COUNCIL_DEBATE_MAX = 3 # debate doubles model calls; tighter cap bounds cost
|
| 72 |
COUNCIL_DEFAULT = ["Amaru", "Kamachiq", "Qhatuq"] # architect · orchestrator · markets
|
| 73 |
|
| 74 |
# One process-wide lounge (in-memory, honest source labels).
|
|
|
|
| 135 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 136 |
<title>Ayllu — a11oy agent council</title>
|
| 137 |
<style>
|
| 138 |
+
:root{--void:#080c14;--panel:#0d1520;--line:#16202c;--teal:#3af4c8;--fg:#dfe7ee;--dim:#7f93a6;--gold:#d4a444}
|
| 139 |
*{box-sizing:border-box}
|
| 140 |
body{margin:0;background:var(--void);color:var(--fg);
|
| 141 |
font:15px/1.55 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
|
| 142 |
+
body::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
|
| 143 |
+
background:radial-gradient(60% 40% at 70% -10%,rgba(58,244,200,.07),transparent 60%),
|
| 144 |
+
radial-gradient(50% 35% at 10% 110%,rgba(212,164,68,.05),transparent 60%)}
|
| 145 |
+
main{max-width:980px;margin:0 auto;padding:36px 22px;position:relative;z-index:1}
|
| 146 |
h1{color:var(--teal);margin:0 0 4px;font-size:26px;display:flex;align-items:center;gap:10px}
|
| 147 |
h2{font-size:16px;margin:0 0 10px;color:var(--fg)}
|
| 148 |
.sub{color:var(--dim);margin:0 0 22px}
|
|
|
|
| 164 |
font-weight:600;font-size:12px}
|
| 165 |
.hint{color:var(--dim);font-size:12px;margin:0 0 8px}
|
| 166 |
.out{margin-top:10px}
|
| 167 |
+
.turn{border:1px solid var(--line);border-radius:9px;padding:10px 12px;margin-top:8px;background:#0a121c}
|
| 168 |
+
.turnh{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
|
| 169 |
+
.chip{width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
|
| 170 |
+
font-size:11px;font-weight:800;color:var(--fg);border:1px solid var(--line);flex:none}
|
| 171 |
+
.meta{color:var(--dim);font-size:12px;margin-left:auto}
|
| 172 |
+
.arch{color:var(--dim);font-size:12px}
|
| 173 |
+
.ans{margin-top:7px;white-space:pre-wrap}
|
| 174 |
.rcpt{color:var(--dim);font-size:12px;margin-top:8px}
|
| 175 |
.note{color:#da3;font-size:12px;margin-bottom:8px}
|
| 176 |
.err{color:#e66}
|
| 177 |
.stub{color:#da3;font-weight:700;font-size:11px}
|
| 178 |
+
.roundhdr{margin:14px 0 2px;color:var(--gold);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}
|
| 179 |
+
.loopchip{font-size:10px;border:1px solid var(--line);border-radius:12px;padding:1px 7px;color:var(--dim)}
|
| 180 |
table{width:100%;border-collapse:collapse}
|
| 181 |
th,td{text-align:left;padding:7px 9px;border-bottom:1px solid var(--line);font-size:14px}
|
| 182 |
th{color:var(--teal);font-weight:600}
|
|
|
|
| 187 |
code{color:var(--teal)}
|
| 188 |
html{scroll-behavior:smooth}
|
| 189 |
.topbar{position:sticky;top:0;z-index:20;background:rgba(8,12,20,.92);backdrop-filter:blur(6px);border-bottom:1px solid var(--line)}
|
| 190 |
+
.tb-wrap{max-width:980px;margin:0 auto;padding:10px 22px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
| 191 |
.tb-brand{color:var(--teal);font-weight:800;text-decoration:none;font-size:18px;display:flex;align-items:center;gap:8px}
|
| 192 |
.tb-nav{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
| 193 |
.tb-nav a{color:var(--dim);text-decoration:none;font-size:13px}
|
| 194 |
.tb-nav a:hover{color:var(--fg)}
|
| 195 |
+
.tb-nav a.tb-home{color:var(--gold);font-weight:600}
|
| 196 |
section[id]{scroll-margin-top:72px}
|
| 197 |
+
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px}
|
| 198 |
+
.panel{background:#0a121c;border:1px solid var(--line);border-radius:8px;padding:10px 12px;min-width:0}
|
| 199 |
+
.panel h3{margin:0 0 6px;font-size:13px;color:var(--teal);display:flex;justify-content:space-between;gap:8px;align-items:baseline}
|
| 200 |
+
.kpi{font-size:19px;font-weight:800;color:var(--fg)}
|
| 201 |
+
.small{font-size:12px;color:var(--dim)}
|
| 202 |
+
.links{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
|
| 203 |
+
.links a{color:var(--teal);text-decoration:none;font-size:12px;border:1px solid var(--line);border-radius:16px;padding:4px 11px}
|
| 204 |
+
.links a:hover{border-color:var(--teal)}
|
| 205 |
+
.tgl{display:flex;gap:7px;align-items:center;color:var(--dim);font-size:13px;margin:0 0 8px}
|
| 206 |
+
.tgl input{width:auto}
|
| 207 |
+
.prov{color:var(--dim);font-size:11px;margin-top:14px;line-height:1.6}
|
| 208 |
</style></head><body>
|
| 209 |
<header class="topbar"><div class="tb-wrap">
|
| 210 |
<a class="tb-brand" href="/ayllu">Ayllu <span id="badge" class="badge">…</span></a>
|
| 211 |
+
<nav class="tb-nav"><a href="#sec-ask">Ask</a><a href="#sec-council">Council</a><a href="#sec-roster">Roster</a><a href="#sec-lounge">Lounge</a><a href="#sec-organism">Organism</a><a href="#sec-mesh">Mesh</a><a class="tb-home" href="/console" title="Back to the a11oy command centre">← a11oy command centre</a></nav>
|
| 212 |
</div></header>
|
| 213 |
<main>
|
| 214 |
<h1>Ayllu</h1>
|
| 215 |
<p class="sub">The AlloyScape tribe, ingested and reborn as a11oy's own agent community —
|
| 216 |
+
<span id="count">?</span> personas, one guarded loop. v__VERSION__ ·
|
| 217 |
+
<span title="Curated, cited text appended to every persona's system prompt — no weights changed anywhere.">knowledge instilled, never "trained"</span></p>
|
| 218 |
|
| 219 |
<section class="card" id="sec-ask">
|
| 220 |
<h2>Ask a persona</h2>
|
|
|
|
| 231 |
<section class="card" id="sec-council">
|
| 232 |
<h2>Convene a council</h2>
|
| 233 |
<p class="hint">Defaults to 3 core personas; select up to 5 (⌘/Ctrl-click). Fan-out is
|
| 234 |
+
capped to protect cost. Debate mode runs exactly two bounded rounds
|
| 235 |
+
(after arXiv:2305.14325) and is capped to 3 personas.</p>
|
| 236 |
<select id="councilsel" multiple size="6"></select>
|
| 237 |
+
<label class="tgl"><input type="checkbox" id="debate">
|
| 238 |
+
Debate mode — positions, then explicit dissent & converge (2× cost)</label>
|
| 239 |
<textarea id="councilprompt" rows="3" placeholder="A question for the council…"></textarea>
|
| 240 |
<button id="councilbtn">Convene</button>
|
| 241 |
<div id="councilout" class="out"></div>
|
|
|
|
| 244 |
<section class="card" id="sec-roster">
|
| 245 |
<h2>Roster</h2>
|
| 246 |
<table id="roster"><thead><tr><th>Persona</th><th>Quechua</th><th>Archetype</th>
|
| 247 |
+
<th>a11oy domain</th><th>Autonomy</th><th>Knowledge</th></tr></thead><tbody></tbody></table>
|
| 248 |
+
<div class="src" style="margin-top:6px">"Instilled ✓" = the shared, cited Wave-13 leaders
|
| 249 |
+
corpus is appended to that persona's system prompt at runtime. No model weights are
|
| 250 |
+
changed anywhere — this is knowledge instillation, not training.</div>
|
| 251 |
</section>
|
| 252 |
|
| 253 |
<section class="card" id="sec-lounge">
|
|
|
|
| 255 |
<div id="lounge" class="out"></div>
|
| 256 |
</section>
|
| 257 |
|
| 258 |
+
<section class="card" id="sec-organism">
|
| 259 |
+
<h2>The organism — anatomy · brain · formulas</h2>
|
| 260 |
+
<div class="src" style="margin:.2rem 0 .6rem">The council is one organ of one governed
|
| 261 |
+
organism. These panels read the same governed endpoints as the command centre — an
|
| 262 |
+
unavailable endpoint says so rather than faking a value.</div>
|
| 263 |
+
<div class="grid">
|
| 264 |
+
<div class="panel"><h3>Formulas <span class="small" id="f-badge">…</span></h3><div id="f-out" class="small">loading…</div></div>
|
| 265 |
+
<div class="panel"><h3>Doctrine lock <span class="small" id="d-badge">…</span></h3><div id="d-out" class="small">loading…</div></div>
|
| 266 |
+
<div class="panel"><h3>Sovereign energy <span class="small" id="e-badge">…</span></h3><div id="e-out" class="small">loading…</div></div>
|
| 267 |
+
</div>
|
| 268 |
+
<div class="links">
|
| 269 |
+
<a href="/living-anatomy">Living anatomy</a>
|
| 270 |
+
<a href="/formulas">PURIQ formulas</a>
|
| 271 |
+
<a href="/wires">The constitution</a>
|
| 272 |
+
<a href="/api/__NS__/v1/brain/graph" title="Full brain graph JSON (~4 MB)">Brain graph (raw JSON)</a>
|
| 273 |
+
<a href="/console">Command centre</a>
|
| 274 |
+
</div>
|
| 275 |
+
</section>
|
| 276 |
+
|
| 277 |
<section class="card" id="sec-mesh">
|
| 278 |
<h2>Mesh & observability <span class="stub" id="mesh-badge">…</span></h2>
|
| 279 |
<div class="src" style="margin:.2rem 0 .6rem">Live platform context the council runs
|
|
|
|
| 288 |
"always execute" mandate is deliberately <b>not</b> adopted. Answers come from a11oy's
|
| 289 |
own model backend + router; when no inference credential is set on this Space,
|
| 290 |
<code>ask</code>/<code>council</code> return a clearly-labeled stub — never a fabricated
|
| 291 |
+
answer. Debate mode is bounded to exactly two rounds. These turns do direct completion
|
| 292 |
+
only (no tool dispatch yet).</div>
|
| 293 |
+
|
| 294 |
+
<p class="prov"><b>Provenance.</b> Council patterns studied from the field's leaders and
|
| 295 |
+
rebuilt in a11oy's own idiom — MetaGPT (SOP role handoffs), CrewAI (role/goal remits),
|
| 296 |
+
LangGraph (bounded graph loops), CAMEL (agent societies), OpenAI Agents SDK / Google ADK
|
| 297 |
+
(visible handoffs & guardrails) — all permissive-licensed; debate-then-converge after
|
| 298 |
+
arXiv:2305.14325. No code was copied from pattern-only sources. Personas carry this as
|
| 299 |
+
instilled knowledge (cited text in the system prompt); nothing here was "trained".</p>
|
| 300 |
</main>
|
| 301 |
<script>
|
| 302 |
const NS="__NS__";
|
| 303 |
const api = p => `/api/${NS}/v1/ayllu/`+p;
|
| 304 |
+
const gapi = p => `/api/${NS}/v1/`+p;
|
| 305 |
async function j(url,opts){const r=await fetch(url,opts);
|
| 306 |
let d={};try{d=await r.json();}catch(e){}return {ok:r.ok,status:r.status,data:d};}
|
| 307 |
function esc(s){return (s==null?'':String(s)).replace(/[&<>]/g,
|
| 308 |
c=>({'&':'&','<':'<','>':'>'}[c]));}
|
| 309 |
+
function hue(n){let h=0;for(const c of String(n))h=(h*31+c.charCodeAt(0))%360;return h;}
|
| 310 |
+
function chip(n){const h=hue(n);
|
| 311 |
+
return `<span class="chip" style="background:hsl(${h} 55% 20%);border-color:hsl(${h} 55% 38%)">`
|
| 312 |
+
+ esc(String(n).slice(0,2).toUpperCase())+`</span>`;}
|
| 313 |
function renderTurn(t){
|
| 314 |
const ans = t.answer!=null ? esc(t.answer) : '<i>'+esc(t.honesty)+'</i>';
|
| 315 |
const model = t.model?('model: '+esc(t.model)):'no model';
|
| 316 |
const tier = (t.tier&&t.tier.route)?(' · tier(advisory): '+esc(t.tier.route)):'';
|
| 317 |
const stub = t.stub?' · <span class="stub">STUB</span>':'';
|
| 318 |
+
const loop = (t.loop&&t.loop.mode)?`<span class="loopchip" title="${esc(t.loop.note||'')}">loop: ${esc(t.loop.mode)}</span>`:'';
|
| 319 |
+
return `<div class="turn"><div class="turnh">${chip(t.persona)}<b>${esc(t.persona)}</b>`
|
| 320 |
+
+ `<span class="arch">${esc(t.archetype||'')}</span>${loop}`
|
| 321 |
+ `<span class="meta">${model}${tier}${stub}</span></div>`
|
| 322 |
+ `<div class="ans">${ans}</div></div>`;
|
| 323 |
}
|
|
|
|
| 335 |
const o=document.createElement('option');o.value=p.name;
|
| 336 |
o.textContent=p.name+' — '+p.domain;sel.appendChild(o);
|
| 337 |
csel.appendChild(o.cloneNode(true));
|
| 338 |
+
const kn=p.knowledge_instilled?'instilled ✓':'—';
|
| 339 |
+
rows.push(`<tr><td>${chip(p.name)} <b>${esc(p.name)}</b></td><td>${esc(p.quechua)}</td>`
|
| 340 |
+`<td>${esc(p.archetype)}</td><td>${esc(p.domain)}</td>`
|
| 341 |
+
+`<td>${esc(p.autonomy_level)}</td><td class="src">${kn}</td></tr>`);
|
| 342 |
});
|
| 343 |
document.querySelector('#roster tbody').innerHTML=rows.join('');
|
| 344 |
}
|
|
|
|
| 363 |
const out=document.getElementById('councilout');
|
| 364 |
if(!prompt){out.innerHTML='<span class="err">enter a prompt</span>';return;}
|
| 365 |
const picks=[...document.getElementById('councilsel').selectedOptions].map(o=>o.value);
|
| 366 |
+
const debate=document.getElementById('debate').checked;
|
| 367 |
+
out.textContent=debate?'…convening (debate: 2 bounded rounds)':'…convening';
|
| 368 |
+
const body={prompt}; if(picks.length)body.personas=picks; if(debate)body.debate=true;
|
| 369 |
const {ok,status,data}=await j(api('council'),{method:'POST',
|
| 370 |
headers:{'content-type':'application/json'},body:JSON.stringify(body)});
|
| 371 |
if(!ok){out.innerHTML='<span class="err">'+esc(data.error||('HTTP '+status))+'</span>'
|
| 372 |
+(data.retry_after_s?(' (retry in '+data.retry_after_s+'s)'):'');return;}
|
| 373 |
+
const res=data.result||{}, rounds=res.rounds||[];
|
| 374 |
+
const cap=res.cap_note?('<div class="note">'+esc(res.cap_note)+'</div>'):'';
|
| 375 |
+
const r1=rounds.filter(t=>(t.round||1)===1), r2=rounds.filter(t=>t.round===2);
|
| 376 |
+
let html=cap;
|
| 377 |
+
if(r2.length){
|
| 378 |
+
html+='<div class="roundhdr">Round 1 — opening positions</div>'+r1.map(renderTurn).join('');
|
| 379 |
+
html+='<div class="roundhdr">Round 2 — debate & converge (final)</div>'+r2.map(renderTurn).join('');
|
| 380 |
+
}else{
|
| 381 |
+
html+=r1.map(renderTurn).join('');
|
| 382 |
+
if(res.mode==='single-round'&&document.getElementById('debate').checked){
|
| 383 |
+
html+='<div class="src">debate skipped honestly: fewer than two personas produced answers.</div>';
|
| 384 |
+
}
|
| 385 |
+
}
|
| 386 |
+
out.innerHTML=html;
|
| 387 |
};
|
| 388 |
async function loadLounge(){
|
| 389 |
const {ok,data}=await j(api('lounge')), out=document.getElementById('lounge');
|
|
|
|
| 394 |
:'<i>empty</i>';
|
| 395 |
}
|
| 396 |
document.getElementById('refreshlounge').onclick=loadLounge;
|
| 397 |
+
function unavailable(el,status){el.innerHTML='<span class="src">endpoint unavailable ('
|
| 398 |
+
+esc(String(status))+') — shown honestly, not faked.</span>';}
|
| 399 |
+
async function loadFormulas(){
|
| 400 |
+
const b=document.getElementById('f-badge'), out=document.getElementById('f-out');
|
| 401 |
+
const {ok,status,data}=await j(gapi('formulas'));
|
| 402 |
+
if(!ok){b.textContent='offline';unavailable(out,status);return;}
|
| 403 |
+
b.textContent='live';
|
| 404 |
+
const fs=(data.formulas||[]).slice(0,4);
|
| 405 |
+
out.innerHTML='<div class="kpi">'+esc(String(data.count??'—'))+'</div>'
|
| 406 |
+
+'<div class="small">registered formulas</div>'
|
| 407 |
+
+fs.map(f=>`<div class="lg"><code>${esc(f.name)}</code><br><span class="src">${esc(f.proof_status||'')}</span></div>`).join('')
|
| 408 |
+
+'<div class="src" style="margin-top:6px">Λ-aggregator uniqueness remains Conjecture 1 — never claimed proven.</div>';
|
| 409 |
+
}
|
| 410 |
+
async function loadDoctrine(){
|
| 411 |
+
const b=document.getElementById('d-badge'), out=document.getElementById('d-out');
|
| 412 |
+
const {ok,status,data}=await j(gapi('honest'));
|
| 413 |
+
if(!ok){b.textContent='offline';unavailable(out,status);return;}
|
| 414 |
+
const d=data.doctrine_lock||{};
|
| 415 |
+
b.textContent=esc(d.state||'?');
|
| 416 |
+
out.innerHTML='<div class="kpi">'+esc(String(d.declarations??'—'))+'</div>'
|
| 417 |
+
+'<div class="small">Lean declarations · '+esc(String(d.axioms??'—'))+' axioms · '
|
| 418 |
+
+esc(String(d.sorries??'—'))+' sorries (honest count)</div>'
|
| 419 |
+
+'<div class="lg small">'+esc(d.lambda_note||'')+'</div>';
|
| 420 |
+
}
|
| 421 |
+
async function loadEnergy(){
|
| 422 |
+
const b=document.getElementById('e-badge'), out=document.getElementById('e-out');
|
| 423 |
+
const {ok,status,data}=await j(gapi('energy/live'));
|
| 424 |
+
if(!ok){b.textContent='offline';unavailable(out,status);return;}
|
| 425 |
+
b.textContent=esc(data.label||'?');
|
| 426 |
+
const nodes=data.nodes||[];
|
| 427 |
+
const live=nodes.filter(n=>n.live).length;
|
| 428 |
+
out.innerHTML='<div class="kpi">'+(data.total_watts!=null?esc(String(data.total_watts))+' W':'—')+'</div>'
|
| 429 |
+
+'<div class="small">'+live+'/'+nodes.length+' sovereign nodes live'
|
| 430 |
+
+(data.total_watts==null?' — no wattage fabricated':'')+'</div>'
|
| 431 |
+
+nodes.slice(0,3).map(n=>`<div class="lg small">${n.live?'●':'○'} ${esc(n.name)}</div>`).join('');
|
| 432 |
+
}
|
| 433 |
async function loadMesh(){
|
| 434 |
const badge=document.getElementById('mesh-badge'), out=document.getElementById('mesh-out');
|
| 435 |
+
const {ok,status,data}=await j(gapi('mesh/state'));
|
| 436 |
+
if(!ok){badge.textContent='offline';unavailable(out,status);return;}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
badge.textContent='live';
|
| 438 |
+
const wires=data.wires||{};
|
| 439 |
+
const rows=Object.entries(wires).map(([k,w])=>
|
| 440 |
+
`<tr><td><b>${esc(k)}</b></td><td>${esc(w.edge||'')}</td><td class="src">${esc(w.status||'')}</td></tr>`);
|
| 441 |
+
out.innerHTML=rows.length?('<table><thead><tr><th>Wire</th><th>Edge</th><th>Status</th></tr></thead><tbody>'
|
| 442 |
+
+rows.join('')+'</tbody></table>'
|
| 443 |
+
+'<div class="src" style="margin-top:5px">doctrine '+esc(data.doctrine||'?')
|
| 444 |
+
+' · khipu nodes: '+esc(String(data.khipu_nodes??'—'))+'</div>')
|
| 445 |
+
:'<div class="lg"><b>mesh</b> <span class="src">no wires reported</span></div>';
|
| 446 |
}
|
| 447 |
async function loadObs(){
|
| 448 |
const out=document.getElementById('obs-out');
|
| 449 |
+
const {ok,status,data}=await j(gapi('observability/summary'));
|
| 450 |
+
if(!ok){unavailable(out,status);return;}
|
|
|
|
| 451 |
out.innerHTML='<div class="lg"><b>observability</b><pre class="src" style="white-space:pre-wrap;margin:.3rem 0 0">'
|
| 452 |
+
+esc(JSON.stringify(data.melt||data,null,2).slice(0,600))+'</pre></div>';
|
| 453 |
}
|
| 454 |
+
loadRoster();loadLounge();loadMesh();loadObs();loadFormulas();loadDoctrine();loadEnergy();
|
| 455 |
</script>
|
| 456 |
</body></html>"""
|
| 457 |
|
|
|
|
| 550 |
return JSONResponse(
|
| 551 |
{"error": f"'prompt' must be a string ≤ {MAX_PROMPT_CHARS} chars",
|
| 552 |
"max_chars": MAX_PROMPT_CHARS}, status_code=422)
|
| 553 |
+
debate = bool(body.get("debate", False))
|
| 554 |
+
max_n = COUNCIL_DEBATE_MAX if debate else COUNCIL_MAX
|
| 555 |
requested = body.get("personas")
|
| 556 |
cap_note = None
|
| 557 |
if requested:
|
| 558 |
if not isinstance(requested, list):
|
| 559 |
return JSONResponse({"error": "'personas' must be a list"},
|
| 560 |
status_code=422)
|
| 561 |
+
names = [str(n) for n in requested][:max_n]
|
| 562 |
+
if len(requested) > max_n:
|
| 563 |
cap_note = (f"requested {len(requested)} personas; capped to "
|
| 564 |
+
f"{max_n} to bound cost"
|
| 565 |
+
+ (" (debate mode runs two rounds)" if debate else ""))
|
| 566 |
else:
|
| 567 |
+
names = list(COUNCIL_DEFAULT)[:max_n]
|
| 568 |
cap_note = (f"no personas specified; convened the {len(names)} core "
|
| 569 |
"personas (Amaru, Kamachiq, Qhatuq)")
|
| 570 |
personas = [get_persona(n) for n in names]
|
|
|
|
| 574 |
{"error": "no known personas in request",
|
| 575 |
"known": [x.name for x in ROSTER]}, status_code=422)
|
| 576 |
result = await _LOUNGE.deliberate(prompt, personas,
|
| 577 |
+
model_complete=_backend.model_complete,
|
| 578 |
+
debate=debate)
|
| 579 |
if cap_note:
|
| 580 |
result["cap_note"] = cap_note
|
| 581 |
council_id = str(uuid.uuid4())
|
|
|
|
| 583 |
"council_id": council_id,
|
| 584 |
"prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
|
| 585 |
"participants": result["participants"],
|
| 586 |
+
"mode": result.get("mode"),
|
| 587 |
"models": [r.get("model") for r in result.get("rounds", [])],
|
| 588 |
})
|
| 589 |
return JSONResponse({"council_id": council_id, "result": result,
|
|
|
|
| 604 |
summary="Ask one persona — bounded, honest, receipted")
|
| 605 |
app.add_api_route(f"/api/{ns}/v1/ayllu/council", _council, methods=["POST"],
|
| 606 |
tags=["ayllu"],
|
| 607 |
+
summary="Bounded multi-persona deliberation (capped fan-out; optional 2-round debate mode after arXiv:2305.14325)")
|
| 608 |
app.add_api_route(f"/api/{ns}/v1/ayllu/lounge", _lounge_feed, methods=["GET"],
|
| 609 |
tags=["ayllu"], summary="Recent collaboration lounge feed")
|
| 610 |
app.add_api_route("/ayllu", _page, methods=["GET"], include_in_schema=False)
|
|
|
|
| 612 |
return (
|
| 613 |
f"ok — ayllu registered: {len(ROSTER)} personas; live model backend "
|
| 614 |
f"({_backend.backend_status().get('mode')}); bounded-autonomy Λ-gate; "
|
| 615 |
+
f"/ayllu + /api/{ns}/v1/ayllu/roster|ask|council|lounge; "
|
| 616 |
+
f"debate-mode council; version={__version__}"
|
| 617 |
)
|
ayllu/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ Public surface:
|
|
| 18 |
"""
|
| 19 |
from __future__ import annotations
|
| 20 |
|
| 21 |
-
__version__ = "0.
|
| 22 |
NAMESPACE_DEFAULT = "a11oy"
|
| 23 |
|
| 24 |
__all__ = ["__version__", "NAMESPACE_DEFAULT"]
|
|
|
|
| 18 |
"""
|
| 19 |
from __future__ import annotations
|
| 20 |
|
| 21 |
+
__version__ = "0.2.0"
|
| 22 |
NAMESPACE_DEFAULT = "a11oy"
|
| 23 |
|
| 24 |
__all__ = ["__version__", "NAMESPACE_DEFAULT"]
|
ayllu/lounge.py
CHANGED
|
@@ -35,6 +35,7 @@ class Lounge:
|
|
| 35 |
model_complete=None,
|
| 36 |
difficulty: float = 0.6,
|
| 37 |
two_person_attested: bool = False,
|
|
|
|
| 38 |
) -> dict[str, Any]:
|
| 39 |
from .loop import run_turn
|
| 40 |
|
|
@@ -46,13 +47,57 @@ class Lounge:
|
|
| 46 |
difficulty=difficulty,
|
| 47 |
two_person_attested=two_person_attested,
|
| 48 |
)
|
|
|
|
| 49 |
src = "brain" if turn.get("answer") is not None else "persona-fallback"
|
| 50 |
self.post(p.name, turn.get("answer") or turn.get("honesty"), source=src)
|
| 51 |
rounds.append(turn)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
return {
|
| 53 |
"prompt": prompt,
|
| 54 |
"participants": [p.name for p in personas],
|
| 55 |
"rounds": rounds,
|
| 56 |
-
"
|
| 57 |
-
|
|
|
|
|
|
|
| 58 |
}
|
|
|
|
| 35 |
model_complete=None,
|
| 36 |
difficulty: float = 0.6,
|
| 37 |
two_person_attested: bool = False,
|
| 38 |
+
debate: bool = False,
|
| 39 |
) -> dict[str, Any]:
|
| 40 |
from .loop import run_turn
|
| 41 |
|
|
|
|
| 47 |
difficulty=difficulty,
|
| 48 |
two_person_attested=two_person_attested,
|
| 49 |
)
|
| 50 |
+
turn["round"] = 1
|
| 51 |
src = "brain" if turn.get("answer") is not None else "persona-fallback"
|
| 52 |
self.post(p.name, turn.get("answer") or turn.get("honesty"), source=src)
|
| 53 |
rounds.append(turn)
|
| 54 |
+
|
| 55 |
+
mode = "single-round"
|
| 56 |
+
# Debate-then-converge (after arXiv:2305.14325, Multiagent Debate): one
|
| 57 |
+
# bounded extra round where each persona reads its peers' round-1 positions,
|
| 58 |
+
# states dissent or agreement explicitly, and revises. EXACTLY two rounds —
|
| 59 |
+
# never an open-ended loop. Runs only when a real/stub backend is injected
|
| 60 |
+
# and at least two personas answered (an honest debate needs peers).
|
| 61 |
+
if debate and model_complete is not None:
|
| 62 |
+
positions = [
|
| 63 |
+
(r.get("persona"), r.get("answer"))
|
| 64 |
+
for r in rounds if r.get("answer")
|
| 65 |
+
]
|
| 66 |
+
if len(positions) >= 2:
|
| 67 |
+
mode = "debate"
|
| 68 |
+
for p in personas:
|
| 69 |
+
peers = "\n\n".join(
|
| 70 |
+
f"[{name}] {ans[:1200]}" for name, ans in positions
|
| 71 |
+
if name != p.name
|
| 72 |
+
)
|
| 73 |
+
debate_prompt = (
|
| 74 |
+
"COUNCIL DEBATE ROUND (bounded: this is the final round).\n"
|
| 75 |
+
f"Original question:\n{prompt}\n\n"
|
| 76 |
+
f"Your peers' opening positions:\n{peers}\n\n"
|
| 77 |
+
"State explicitly where you agree or dissent, then give "
|
| 78 |
+
"your revised answer. Honest dissent beats false consensus."
|
| 79 |
+
)
|
| 80 |
+
turn = await run_turn(
|
| 81 |
+
p, debate_prompt,
|
| 82 |
+
model_complete=model_complete,
|
| 83 |
+
difficulty=difficulty,
|
| 84 |
+
two_person_attested=two_person_attested,
|
| 85 |
+
)
|
| 86 |
+
turn["round"] = 2
|
| 87 |
+
src = ("brain" if turn.get("answer") is not None
|
| 88 |
+
else "persona-fallback")
|
| 89 |
+
self.post(p.name, turn.get("answer") or turn.get("honesty"),
|
| 90 |
+
source=src)
|
| 91 |
+
rounds.append(turn)
|
| 92 |
+
else:
|
| 93 |
+
mode = "single-round"
|
| 94 |
+
|
| 95 |
return {
|
| 96 |
"prompt": prompt,
|
| 97 |
"participants": [p.name for p in personas],
|
| 98 |
"rounds": rounds,
|
| 99 |
+
"mode": mode,
|
| 100 |
+
"note": "bounded council; each turn honest (no fabrication when a model "
|
| 101 |
+
"backend is absent); debate mode = exactly two rounds, "
|
| 102 |
+
"after arXiv:2305.14325",
|
| 103 |
}
|
ayllu/personas.py
CHANGED
|
@@ -33,9 +33,16 @@ class Persona:
|
|
| 33 |
|
| 34 |
def system_prompt(self) -> str:
|
| 35 |
p = self.soul_path()
|
| 36 |
-
if
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
def metadata(self) -> dict:
|
| 41 |
return {
|
|
@@ -47,6 +54,7 @@ class Persona:
|
|
| 47 |
"approval_required": self.approval_required,
|
| 48 |
"default_difficulty": self.default_difficulty,
|
| 49 |
"soul_present": self.soul_path().exists(),
|
|
|
|
| 50 |
}
|
| 51 |
|
| 52 |
|
|
|
|
| 33 |
|
| 34 |
def system_prompt(self) -> str:
|
| 35 |
p = self.soul_path()
|
| 36 |
+
if p.exists():
|
| 37 |
+
prompt = p.read_text(encoding="utf-8")
|
| 38 |
+
else:
|
| 39 |
+
prompt = f"[soul file missing for {self.name}: {p} — honest empty persona]"
|
| 40 |
+
shared = _SOULS_DIR / "_shared_knowledge.md"
|
| 41 |
+
if shared.exists():
|
| 42 |
+
# Knowledge INSTILLED (curated, cited text appended to the system
|
| 43 |
+
# prompt) — never "training"; no weights are changed anywhere.
|
| 44 |
+
prompt += "\n\n" + shared.read_text(encoding="utf-8")
|
| 45 |
+
return prompt
|
| 46 |
|
| 47 |
def metadata(self) -> dict:
|
| 48 |
return {
|
|
|
|
| 54 |
"approval_required": self.approval_required,
|
| 55 |
"default_difficulty": self.default_difficulty,
|
| 56 |
"soul_present": self.soul_path().exists(),
|
| 57 |
+
"knowledge_instilled": (_SOULS_DIR / "_shared_knowledge.md").exists(),
|
| 58 |
}
|
| 59 |
|
| 60 |
|