Spaces:
Running on Zero
Running on Zero
refactor: rename planner targets to goals
Browse filesCo-authored-by: Codex <noreply@openai.com>
- DESIGN.md +4 -4
- hackathon_advisor/agent.py +3 -3
- hackathon_advisor/tool_contracts.py +5 -8
- tests/test_agent.py +2 -2
- tests/test_tool_contracts.py +6 -0
DESIGN.md
CHANGED
|
@@ -267,7 +267,7 @@ code.** Keep live context to ~800β1200 tokens of *curated* view, never raw dat
|
|
| 267 |
Few tools, few params each, short descriptions (1B-friendly). Heavy logic in code.
|
| 268 |
|
| 269 |
**Jargon alias layer (input normalization).** Before any tool call and before display, run ASR/user text through a
|
| 270 |
-
deterministic fuzzy/alias map over our small CLOSED vocab (model names
|
| 271 |
`token_set_ratio` / double-metaphone β mapping "neutron"/"nemo tron" β Nemotron, "mini cpm" β MiniCPM5, "zero gpu" β
|
| 272 |
ZeroGPU. Surface the correction ("heard: neutron β Nemotron") as a trust-building, slightly delightful moment.
|
| 273 |
|
|
@@ -287,12 +287,12 @@ into a local snapshot + EmbeddingGemma index (keeps Off the Grid at runtime).
|
|
| 287 |
|
| 288 |
| Tool | Signature | Purpose |
|
| 289 |
|---|---|---|
|
| 290 |
-
| `save_idea` | `(title, pitch
|
| 291 |
| `score_idea` | `(id)` | fixed (hardcoded) rubric β scores + gaps; the 1B only triggers + verbalizes |
|
| 292 |
| `compare_ideas` | `()` | rank the board, articulate tradeoffs |
|
| 293 |
-
| `make_plan` | `(id)` | build plan +
|
| 294 |
| `update_profile` | `(field, value)` | record skills/time/prefs β Layer B |
|
| 295 |
-
| `
|
| 296 |
|
| 297 |
---
|
| 298 |
|
|
|
|
| 267 |
Few tools, few params each, short descriptions (1B-friendly). Heavy logic in code.
|
| 268 |
|
| 269 |
**Jargon alias layer (input normalization).** Before any tool call and before display, run ASR/user text through a
|
| 270 |
+
deterministic fuzzy/alias map over our small CLOSED vocab (model names and goal names) β e.g. RapidFuzz
|
| 271 |
`token_set_ratio` / double-metaphone β mapping "neutron"/"nemo tron" β Nemotron, "mini cpm" β MiniCPM5, "zero gpu" β
|
| 272 |
ZeroGPU. Surface the correction ("heard: neutron β Nemotron") as a trust-building, slightly delightful moment.
|
| 273 |
|
|
|
|
| 287 |
|
| 288 |
| Tool | Signature | Purpose |
|
| 289 |
|---|---|---|
|
| 290 |
+
| `save_idea` | `(title, pitch)` | add/update a candidate on the idea board |
|
| 291 |
| `score_idea` | `(id)` | fixed (hardcoded) rubric β scores + gaps; the 1B only triggers + verbalizes |
|
| 292 |
| `compare_ideas` | `()` | rank the board, articulate tradeoffs |
|
| 293 |
+
| `make_plan` | `(id)` | build plan + goals the current direction can support |
|
| 294 |
| `update_profile` | `(field, value)` | record skills/time/prefs β Layer B |
|
| 295 |
+
| `set_goals` | `(goals[])` | change selected goals β updates Layer A bias |
|
| 296 |
|
| 297 |
---
|
| 298 |
|
hackathon_advisor/agent.py
CHANGED
|
@@ -128,7 +128,7 @@ class AdvisorEngine:
|
|
| 128 |
if call.name == "update_profile":
|
| 129 |
return self._profile_turn(call, normalized, corrections, state, tool_events)
|
| 130 |
|
| 131 |
-
if call.name == "
|
| 132 |
return self._target_turn(call, normalized, corrections, state, tool_events)
|
| 133 |
|
| 134 |
return self._idea_research_turn(call, normalized, corrections, state, tool_events)
|
|
@@ -355,13 +355,13 @@ class AdvisorEngine:
|
|
| 355 |
state: dict[str, Any],
|
| 356 |
tool_events: list[ToolEvent],
|
| 357 |
) -> TurnResult:
|
| 358 |
-
targets = normalize_targets(call.arguments.get("
|
| 359 |
state["targets"] = targets
|
| 360 |
idea = self._current_idea(state)
|
| 361 |
if idea is not None:
|
| 362 |
idea.targets = targets
|
| 363 |
self._store_idea(state, idea)
|
| 364 |
-
tool_events.append(ToolEvent("
|
| 365 |
labels = [target_label(target) for target in targets]
|
| 366 |
response = "The seal will now bias toward: " + (", ".join(labels) or "no specific goals")
|
| 367 |
return self._result(normalized, corrections, response, state, tool_events, [], [], None, [], {})
|
|
|
|
| 128 |
if call.name == "update_profile":
|
| 129 |
return self._profile_turn(call, normalized, corrections, state, tool_events)
|
| 130 |
|
| 131 |
+
if call.name == "set_goals":
|
| 132 |
return self._target_turn(call, normalized, corrections, state, tool_events)
|
| 133 |
|
| 134 |
return self._idea_research_turn(call, normalized, corrections, state, tool_events)
|
|
|
|
| 355 |
state: dict[str, Any],
|
| 356 |
tool_events: list[ToolEvent],
|
| 357 |
) -> TurnResult:
|
| 358 |
+
targets = normalize_targets(call.arguments.get("goals"), default=[])
|
| 359 |
state["targets"] = targets
|
| 360 |
idea = self._current_idea(state)
|
| 361 |
if idea is not None:
|
| 362 |
idea.targets = targets
|
| 363 |
self._store_idea(state, idea)
|
| 364 |
+
tool_events.append(ToolEvent("set_goals", f"Set {len(targets)} goals."))
|
| 365 |
labels = [target_label(target) for target in targets]
|
| 366 |
response = "The seal will now bias toward: " + (", ".join(labels) or "no specific goals")
|
| 367 |
return self._result(normalized, corrections, response, state, tool_events, [], [], None, [], {})
|
hackathon_advisor/tool_contracts.py
CHANGED
|
@@ -85,7 +85,7 @@ TOOL_SPECS: dict[str, ToolSpec] = {
|
|
| 85 |
name="list_projects",
|
| 86 |
description="Read prominent Build Small project cards from the offline snapshot.",
|
| 87 |
fields={
|
| 88 |
-
"track": ToolField("string", "Optional
|
| 89 |
"sort": ToolField("string", "Sort key.", enum=("likes", "recent", "title")),
|
| 90 |
},
|
| 91 |
),
|
|
@@ -110,9 +110,6 @@ TOOL_SPECS: dict[str, ToolSpec] = {
|
|
| 110 |
fields={
|
| 111 |
"title": ToolField("string", "Short idea title.", required=True),
|
| 112 |
"pitch": ToolField("string", "One-sentence idea pitch.", required=True),
|
| 113 |
-
"track": ToolField("string", "Primary target track or award."),
|
| 114 |
-
"models": ToolField("array", "Model ids the idea may use.", items_type="string"),
|
| 115 |
-
"side_quests": ToolField("array", "Badge or side quest targets.", items_type="string"),
|
| 116 |
},
|
| 117 |
),
|
| 118 |
"score_idea": ToolSpec(
|
|
@@ -143,10 +140,10 @@ TOOL_SPECS: dict[str, ToolSpec] = {
|
|
| 143 |
"value": ToolField("string", "Profile value to remember.", required=True),
|
| 144 |
},
|
| 145 |
),
|
| 146 |
-
"
|
| 147 |
-
name="
|
| 148 |
-
description="Change the
|
| 149 |
-
fields={"
|
| 150 |
),
|
| 151 |
}
|
| 152 |
|
|
|
|
| 85 |
name="list_projects",
|
| 86 |
description="Read prominent Build Small project cards from the offline snapshot.",
|
| 87 |
fields={
|
| 88 |
+
"track": ToolField("string", "Optional model, goal, or topic filter."),
|
| 89 |
"sort": ToolField("string", "Sort key.", enum=("likes", "recent", "title")),
|
| 90 |
},
|
| 91 |
),
|
|
|
|
| 110 |
fields={
|
| 111 |
"title": ToolField("string", "Short idea title.", required=True),
|
| 112 |
"pitch": ToolField("string", "One-sentence idea pitch.", required=True),
|
|
|
|
|
|
|
|
|
|
| 113 |
},
|
| 114 |
),
|
| 115 |
"score_idea": ToolSpec(
|
|
|
|
| 140 |
"value": ToolField("string", "Profile value to remember.", required=True),
|
| 141 |
},
|
| 142 |
),
|
| 143 |
+
"set_goals": ToolSpec(
|
| 144 |
+
name="set_goals",
|
| 145 |
+
description="Change the selected goals used to bias ideation and planning.",
|
| 146 |
+
fields={"goals": ToolField("array", "Goal ids to prioritize.", required=True, items_type="string")},
|
| 147 |
),
|
| 148 |
}
|
| 149 |
|
tests/test_agent.py
CHANGED
|
@@ -180,9 +180,9 @@ def test_planner_profile_and_targets_update_state() -> None:
|
|
| 180 |
profile = profile_engine.turn("remember this", {})
|
| 181 |
target_engine = AdvisorEngine(
|
| 182 |
index,
|
| 183 |
-
planner=StaticPlanner(ToolCall("
|
| 184 |
)
|
| 185 |
-
targeted = target_engine.turn("
|
| 186 |
|
| 187 |
assert targeted.state["profile"]["skills"] == "frontend"
|
| 188 |
assert targeted.state["targets"] == ["Off the Grid", "Field Notes"]
|
|
|
|
| 180 |
profile = profile_engine.turn("remember this", {})
|
| 181 |
target_engine = AdvisorEngine(
|
| 182 |
index,
|
| 183 |
+
planner=StaticPlanner(ToolCall("set_goals", {"goals": ["Off the Grid", "Field Notes"]})),
|
| 184 |
)
|
| 185 |
+
targeted = target_engine.turn("set goals", profile.state)
|
| 186 |
|
| 187 |
assert targeted.state["profile"]["skills"] == "frontend"
|
| 188 |
assert targeted.state["targets"] == ["Off the Grid", "Field Notes"]
|
tests/test_tool_contracts.py
CHANGED
|
@@ -21,7 +21,13 @@ def test_tool_schemas_are_model_ready() -> None:
|
|
| 21 |
"save_idea",
|
| 22 |
"score_idea",
|
| 23 |
"make_plan",
|
|
|
|
| 24 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
|
| 27 |
def test_parse_and_validate_minicpm_xml_tool_call() -> None:
|
|
|
|
| 21 |
"save_idea",
|
| 22 |
"score_idea",
|
| 23 |
"make_plan",
|
| 24 |
+
"set_goals",
|
| 25 |
}
|
| 26 |
+
schema_text = str(schemas)
|
| 27 |
+
assert "set_target" not in schema_text
|
| 28 |
+
assert "side_quests" not in schema_text
|
| 29 |
+
assert "prize" not in schema_text.lower()
|
| 30 |
+
assert "badge" not in schema_text.lower()
|
| 31 |
|
| 32 |
|
| 33 |
def test_parse_and_validate_minicpm_xml_tool_call() -> None:
|