Spaces:
Running on Zero
Running on Zero
fix: invalidate stale plans on context changes
Browse filesCo-authored-by: Codex <noreply@openai.com>
- hackathon_advisor/agent.py +2 -0
- static/app.js +4 -0
- tests/test_agent.py +9 -1
hackathon_advisor/agent.py
CHANGED
|
@@ -355,6 +355,7 @@ class AdvisorEngine:
|
|
| 355 |
field = str(call.arguments["field"])
|
| 356 |
profile[field] = str(call.arguments["value"])
|
| 357 |
state["profile"] = profile
|
|
|
|
| 358 |
tool_events.append(ToolEvent("update_profile", f"Remembered {field}."))
|
| 359 |
response = f"Profile updated: {field} = {profile[field]}."
|
| 360 |
return self._result(normalized, corrections, response, state, tool_events, [], [], None, [], {})
|
|
@@ -369,6 +370,7 @@ class AdvisorEngine:
|
|
| 369 |
) -> TurnResult:
|
| 370 |
goals = normalize_goals(call.arguments.get("goals"), default=[])
|
| 371 |
state["goals"] = goals
|
|
|
|
| 372 |
idea = self._current_idea(state)
|
| 373 |
if idea is not None:
|
| 374 |
idea.goals = goals
|
|
|
|
| 355 |
field = str(call.arguments["field"])
|
| 356 |
profile[field] = str(call.arguments["value"])
|
| 357 |
state["profile"] = profile
|
| 358 |
+
state.pop("last_plan", None)
|
| 359 |
tool_events.append(ToolEvent("update_profile", f"Remembered {field}."))
|
| 360 |
response = f"Profile updated: {field} = {profile[field]}."
|
| 361 |
return self._result(normalized, corrections, response, state, tool_events, [], [], None, [], {})
|
|
|
|
| 370 |
) -> TurnResult:
|
| 371 |
goals = normalize_goals(call.arguments.get("goals"), default=[])
|
| 372 |
state["goals"] = goals
|
| 373 |
+
state.pop("last_plan", None)
|
| 374 |
idea = self._current_idea(state)
|
| 375 |
if idea is not None:
|
| 376 |
idea.goals = goals
|
static/app.js
CHANGED
|
@@ -616,6 +616,10 @@ function handleEvent(event) {
|
|
| 616 |
renderScore(event.score);
|
| 617 |
ink.classList.toggle("bleed", event.score.verdict.startsWith("ECHO"));
|
| 618 |
ink.classList.toggle("gold", event.score.verdict.startsWith("UNWRITTEN"));
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
}
|
| 620 |
if (event.artifact?.title) {
|
| 621 |
currentArtifact = event.artifact;
|
|
|
|
| 616 |
renderScore(event.score);
|
| 617 |
ink.classList.toggle("bleed", event.score.verdict.startsWith("ECHO"));
|
| 618 |
ink.classList.toggle("gold", event.score.verdict.startsWith("UNWRITTEN"));
|
| 619 |
+
} else if (!event.projects?.length) {
|
| 620 |
+
const idea = currentIdea();
|
| 621 |
+
renderSelectedIdeaSeal(idea);
|
| 622 |
+
renderSelectedIdeaArtifact(idea || {});
|
| 623 |
}
|
| 624 |
if (event.artifact?.title) {
|
| 625 |
currentArtifact = event.artifact;
|
tests/test_agent.py
CHANGED
|
@@ -181,11 +181,14 @@ def test_planner_get_project_drives_project_response() -> None:
|
|
| 181 |
|
| 182 |
def test_planner_profile_and_goals_update_state() -> None:
|
| 183 |
index = ProjectIndex.from_files(Path("data/projects.json"), Path("data/project_index.json"))
|
|
|
|
|
|
|
|
|
|
| 184 |
profile_engine = AdvisorEngine(
|
| 185 |
index,
|
| 186 |
planner=StaticPlanner(ToolCall("update_profile", {"field": "skills", "value": "frontend"})),
|
| 187 |
)
|
| 188 |
-
profile = profile_engine.turn("remember this",
|
| 189 |
target_engine = AdvisorEngine(
|
| 190 |
index,
|
| 191 |
planner=StaticPlanner(ToolCall("set_goals", {"goals": ["Off the Grid", "Field Notes"]})),
|
|
@@ -194,12 +197,16 @@ def test_planner_profile_and_goals_update_state() -> None:
|
|
| 194 |
|
| 195 |
assert targeted.state["profile"]["skills"] == "frontend"
|
| 196 |
assert targeted.state["goals"] == ["Off the Grid", "Field Notes"]
|
|
|
|
|
|
|
| 197 |
assert "Local-first, Build notes" in targeted.response
|
| 198 |
|
| 199 |
|
| 200 |
def test_goal_update_invalidates_current_idea_artifact() -> None:
|
| 201 |
index = ProjectIndex.from_files(Path("data/projects.json"), Path("data/project_index.json"))
|
| 202 |
first = AdvisorEngine(index).turn("A local-first archive cartographer for family photos", {})
|
|
|
|
|
|
|
| 203 |
target_engine = AdvisorEngine(
|
| 204 |
index,
|
| 205 |
planner=StaticPlanner(ToolCall("set_goals", {"goals": ["Field Notes"]})),
|
|
@@ -211,6 +218,7 @@ def test_goal_update_invalidates_current_idea_artifact() -> None:
|
|
| 211 |
assert idea["score"] is None
|
| 212 |
assert idea["artifact"] is None
|
| 213 |
assert "last_artifact" not in targeted.state
|
|
|
|
| 214 |
|
| 215 |
|
| 216 |
def test_session_goals_apply_to_new_and_current_ideas() -> None:
|
|
|
|
| 181 |
|
| 182 |
def test_planner_profile_and_goals_update_state() -> None:
|
| 183 |
index = ProjectIndex.from_files(Path("data/projects.json"), Path("data/project_index.json"))
|
| 184 |
+
planned = AdvisorEngine(index).turn("A local-first archive cartographer for family photos", {})
|
| 185 |
+
planned = AdvisorEngine(index).turn("make a build plan", planned.state)
|
| 186 |
+
assert planned.state["last_plan"]
|
| 187 |
profile_engine = AdvisorEngine(
|
| 188 |
index,
|
| 189 |
planner=StaticPlanner(ToolCall("update_profile", {"field": "skills", "value": "frontend"})),
|
| 190 |
)
|
| 191 |
+
profile = profile_engine.turn("remember this", planned.state)
|
| 192 |
target_engine = AdvisorEngine(
|
| 193 |
index,
|
| 194 |
planner=StaticPlanner(ToolCall("set_goals", {"goals": ["Off the Grid", "Field Notes"]})),
|
|
|
|
| 197 |
|
| 198 |
assert targeted.state["profile"]["skills"] == "frontend"
|
| 199 |
assert targeted.state["goals"] == ["Off the Grid", "Field Notes"]
|
| 200 |
+
assert "last_plan" not in profile.state
|
| 201 |
+
assert "last_plan" not in targeted.state
|
| 202 |
assert "Local-first, Build notes" in targeted.response
|
| 203 |
|
| 204 |
|
| 205 |
def test_goal_update_invalidates_current_idea_artifact() -> None:
|
| 206 |
index = ProjectIndex.from_files(Path("data/projects.json"), Path("data/project_index.json"))
|
| 207 |
first = AdvisorEngine(index).turn("A local-first archive cartographer for family photos", {})
|
| 208 |
+
first = AdvisorEngine(index).turn("make a build plan", first.state)
|
| 209 |
+
assert first.state["last_plan"]
|
| 210 |
target_engine = AdvisorEngine(
|
| 211 |
index,
|
| 212 |
planner=StaticPlanner(ToolCall("set_goals", {"goals": ["Field Notes"]})),
|
|
|
|
| 218 |
assert idea["score"] is None
|
| 219 |
assert idea["artifact"] is None
|
| 220 |
assert "last_artifact" not in targeted.state
|
| 221 |
+
assert "last_plan" not in targeted.state
|
| 222 |
|
| 223 |
|
| 224 |
def test_session_goals_apply_to_new_and_current_ideas() -> None:
|