irregular6612 Claude Opus 4.8 (1M context) commited on
Commit
d54ab68
·
1 Parent(s): d1e257a

feat(web): policy memory modes + replay-only panel (drop text) + graceful provider errors

Browse files
proteus/web/server.py CHANGED
@@ -79,13 +79,25 @@ def _resolve_web_memory(
79
  memory_turns=memory_turns, model_name=f"persona:{pid}", persona=persona,
80
  )
81
  return ckpt, False, None
 
 
 
 
 
 
 
 
 
 
 
 
82
  if mode in ("generate", "latest"):
83
  if not model:
84
  return None, False, f"memory mode {mode!r} needs a model"
85
  from proteus.providers import make_provider
86
  try:
87
  provider = make_provider(model)
88
- except ValueError as exc:
89
  return None, False, str(exc)
90
  if mode == "generate":
91
  from proteus.agents.vanilla import VanillaAgent
@@ -212,8 +224,8 @@ def _create_spectate(body: dict, registry: dict) -> Response:
212
  return _err(400, f"unknown scenario {scenario!r}")
213
  try:
214
  provider = make_provider(model)
215
- except ValueError as exc:
216
- return _err(400, str(exc))
217
  memory, use_default, mem_err = _resolve_web_memory(
218
  body.get("memory"), scenario=scenario, difficulty=difficulty, seed=seed,
219
  model=model, memory_root=body.get("memory_root") or "runs/memory",
 
79
  memory_turns=memory_turns, model_name=f"persona:{pid}", persona=persona,
80
  )
81
  return ckpt, False, None
82
+ if mode.startswith("policy:"):
83
+ from proteus.runtime.memory_policies import available_policies, get_policy
84
+ name = mode.split(":", 1)[1]
85
+ try:
86
+ policy = get_policy(name)
87
+ except KeyError:
88
+ return None, False, f"unknown policy {name!r}; have {available_policies()}"
89
+ ckpt = generate_memory(
90
+ scenario, None, difficulty=difficulty, seed=seed,
91
+ memory_turns=memory_turns, model_name=f"policy:{name}", policy=policy,
92
+ )
93
+ return ckpt, False, None
94
  if mode in ("generate", "latest"):
95
  if not model:
96
  return None, False, f"memory mode {mode!r} needs a model"
97
  from proteus.providers import make_provider
98
  try:
99
  provider = make_provider(model)
100
+ except Exception as exc: # missing SDK / key / bad spec -> clear client error
101
  return None, False, str(exc)
102
  if mode == "generate":
103
  from proteus.agents.vanilla import VanillaAgent
 
224
  return _err(400, f"unknown scenario {scenario!r}")
225
  try:
226
  provider = make_provider(model)
227
+ except Exception as exc: # missing SDK / key / bad spec -> clear client error
228
+ return _err(400, f"provider {model!r}: {exc}")
229
  memory, use_default, mem_err = _resolve_web_memory(
230
  body.get("memory"), scenario=scenario, difficulty=difficulty, seed=seed,
231
  model=model, memory_root=body.get("memory_root") or "runs/memory",
proteus/web/static/index.html CHANGED
@@ -42,8 +42,6 @@
42
  #memoryPanel { border:1px solid var(--line); border-radius:8px; margin:8px 0;
43
  background:#0e1017; }
44
  #memoryPanel summary { cursor:pointer; padding:6px 10px; color:var(--fg); }
45
- #memoryBlock { margin:0; padding:0 10px 10px; max-height:240px; overflow:auto;
46
- white-space:pre-wrap; font-size:11px; color:var(--muted); }
47
  </style>
48
  </head>
49
  <body>
@@ -68,6 +66,9 @@
68
  <option value="persona:risk_averse">persona · risk-averse (sample)</option>
69
  <option value="persona:risk_seeking">persona · risk-seeking</option>
70
  <option value="persona:survival_optimal">persona · survival-optimal</option>
 
 
 
71
  <option value="generate">LLM self-play (generate)</option>
72
  <option value="latest">latest saved</option>
73
  </select></div>
@@ -97,7 +98,6 @@
97
  <div id="memReplayGrid" style="display:grid;gap:1px;width:max-content;
98
  background:var(--line);padding:1px;border-radius:4px"></div>
99
  </div>
100
- <pre id="memoryBlock"></pre>
101
  </details>
102
  <div id="grid"></div>
103
  <div id="analysis" class="hidden">
@@ -293,11 +293,10 @@ function showMem(m) {
293
  $("memInfo").textContent =
294
  `memory shown at handover — source: ${m.source}`
295
  + `${m.persona ? " (persona " + m.persona + ")" : ""} · ${m.turns} turns`;
296
- // Show the actual memory the model is given, at the start (expanded).
297
- $("memoryBlock").textContent = m.block || "";
298
  $("memorySummary").textContent =
299
- `memory shown to the model — ${m.source} · ${m.turns} turns (click to collapse)`;
300
- // Replay of how the model actually moved (alongside the text block).
 
301
  MEM_FRAMES = (m && m.frames) || []; MEM_I = 0; MEM_PLAY = false;
302
  $("memReplayPlay").textContent = "▶ play";
303
  const rep = $("memReplay");
 
42
  #memoryPanel { border:1px solid var(--line); border-radius:8px; margin:8px 0;
43
  background:#0e1017; }
44
  #memoryPanel summary { cursor:pointer; padding:6px 10px; color:var(--fg); }
 
 
45
  </style>
46
  </head>
47
  <body>
 
66
  <option value="persona:risk_averse">persona · risk-averse (sample)</option>
67
  <option value="persona:risk_seeking">persona · risk-seeking</option>
68
  <option value="persona:survival_optimal">persona · survival-optimal</option>
69
+ <option value="policy:survival_dynamic">policy · survival (dynamic evade)</option>
70
+ <option value="policy:survival_refuge">policy · survival (hide in refuge)</option>
71
+ <option value="policy:food_rush">policy · food rush (ignore predator)</option>
72
  <option value="generate">LLM self-play (generate)</option>
73
  <option value="latest">latest saved</option>
74
  </select></div>
 
98
  <div id="memReplayGrid" style="display:grid;gap:1px;width:max-content;
99
  background:var(--line);padding:1px;border-radius:4px"></div>
100
  </div>
 
101
  </details>
102
  <div id="grid"></div>
103
  <div id="analysis" class="hidden">
 
293
  $("memInfo").textContent =
294
  `memory shown at handover — source: ${m.source}`
295
  + `${m.persona ? " (persona " + m.persona + ")" : ""} · ${m.turns} turns`;
 
 
296
  $("memorySummary").textContent =
297
+ `memory — ${m.source} · ${m.turns} turns (replay below)`;
298
+ // The memory is shown only as a movement replay (no text block).
299
+ MEM_FRAMES = (m && m.frames) || []; MEM_I = 0; MEM_PLAY = false;
300
  MEM_FRAMES = (m && m.frames) || []; MEM_I = 0; MEM_PLAY = false;
301
  $("memReplayPlay").textContent = "▶ play";
302
  const rep = $("memReplay");
tests/web/test_memory_modes.py CHANGED
@@ -111,3 +111,30 @@ def test_index_html_has_replay_controls():
111
  html = body.decode() if isinstance(body, (bytes, bytearray)) else body
112
  assert "memReplayGrid" in html
113
  assert "memReplayPrev" in html and "memReplayNext" in html and "memReplayPlay" in html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  html = body.decode() if isinstance(body, (bytes, bytearray)) else body
112
  assert "memReplayGrid" in html
113
  assert "memReplayPrev" in html and "memReplayNext" in html and "memReplayPlay" in html
114
+
115
+
116
+ def test_policy_memory_attached_to_session():
117
+ _, payload, _ = server.handle_request(
118
+ "POST", "/session",
119
+ {"scenario": "pack_evade", "seed": 7, "play_turns": 3,
120
+ "memory": "policy:survival_refuge"}, _reg())
121
+ assert payload["memory"]["attached"] is True
122
+ assert payload["memory"]["source"] == "policy:survival_refuge"
123
+ assert payload["memory"]["turns"] >= 1
124
+ assert payload["memory"]["frames"]
125
+
126
+
127
+ def test_unknown_policy_is_400():
128
+ status, payload, _ = server.handle_request(
129
+ "POST", "/session",
130
+ {"scenario": "pack_evade", "memory": "policy:bogus"}, _reg())
131
+ assert status == 400 and "error" in payload
132
+
133
+
134
+ def test_spectate_bad_provider_is_400_not_500():
135
+ # ollama with no key -> ValueError now; must surface as a clean 400, not a 500.
136
+ status, payload, _ = server.handle_request(
137
+ "POST", "/spectate",
138
+ {"scenario": "pack_evade", "seed": 7, "play_turns": 2, "model": "ollama:x",
139
+ "memory": "none"}, _reg())
140
+ assert status == 400 and "error" in payload