Skip to content

Commit e404f25

Browse files
stateofkateRishiDesaidot-agi
committed
new taxonomy (#1023)
* new taxonomy * sync the frontend taxonomy vocabulary with the backend enums Drops thinking_diagnose in favour of thinking_correction, adds implementing_correction, and renames testing_custom_edge_cases to testing_edge_cases. The frontend union, labels, color vars and --tc-* tokens now mirror the flat backend vocabulary, so the new kinds get their fixed semantic color instead of an appearance-order phase slot. The two retired kinds stay mapped on the frontend: stored summaries still carry them, and each shares the slot its replacement took over. --------- Co-authored-by: Rishi Desai <RishiDesai@users.noreply.github.com> Co-authored-by: Pratyush Shukla <ps4534@nyu.edu>
1 parent a569a31 commit e404f25

5 files changed

Lines changed: 120 additions & 51 deletions

File tree

backend/api/services/blocks/analyzer/trajectory/trajectory_component_block.py

Lines changed: 96 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ class ExploreTrajectoryBlockTaxonomy(str, enum.Enum):
2020
THINKING_RECALL = "thinking_recall"
2121
THINKING_UNDERSTAND = "thinking_understand"
2222
THINKING_HYPOTHESIZE = "thinking_hypothesize"
23-
THINKING_DIAGNOSE = "thinking_diagnose"
23+
THINKING_CORRECTION = "thinking_correction"
2424

2525

2626
class ImplementTrajectoryBlockTaxonomy(str, enum.Enum):
2727
IMPLEMENTING = "implementing"
28+
IMPLEMENTING_CORRECTION = "implementing_correction"
2829
WRITING_TESTS = "writing_tests"
2930
TESTING_PUBLIC = "testing_public"
3031
TESTING_CUSTOM = "testing_custom"
31-
TESTING_CUSTOM_EDGE_CASES = "testing_custom_edge_cases"
32+
TESTING_EDGE_CASES = "testing_edge_cases"
3233
DEBUGGING = "debugging"
3334

3435

@@ -84,8 +85,12 @@ def _drop_bad_elements(cls, data):
8485
return data
8586
data = dict(data)
8687
data["summary"] = str(data.get("summary") or "")
87-
data["highlights"] = [h for h in (data.get("highlights") or []) if isinstance(h, dict)]
88-
data["components"] = [c for c in (data.get("components") or []) if isinstance(c, dict)]
88+
data["highlights"] = [
89+
h for h in (data.get("highlights") or []) if isinstance(h, dict)
90+
]
91+
data["components"] = [
92+
c for c in (data.get("components") or []) if isinstance(c, dict)
93+
]
8994
return data
9095

9196

@@ -134,7 +139,9 @@ class TrajectoryBlock(Block):
134139

135140
output_schema = TrajectoryOutput
136141

137-
def __init__(self, trajectory_input: TrajectoryInput, *, instructions_template: str) -> None:
142+
def __init__(
143+
self, trajectory_input: TrajectoryInput, *, instructions_template: str
144+
) -> None:
138145
self.trajectory_input = trajectory_input
139146
self._instructions_template = instructions_template
140147

@@ -143,37 +150,66 @@ def sections(self) -> list[dict]:
143150
ti = self.trajectory_input
144151
taxonomy_values = [m.value for m in TrajectoryBlockTaxonomy]
145152
return [
146-
{"name": "preamble", "raw_input": {}, "schema": _PreambleIn,
147-
"formatter": lambda _d: tp.PREAMBLE},
148-
{"name": "task",
149-
"raw_input": {"task_name": ti.task_name, "instruction": ti.instruction},
150-
"schema": _TaskIn, "formatter": self._fmt_task},
151-
{"name": "outcome",
152-
"raw_input": {"final_reward": ti.final_reward, "model_used": ti.model_used,
153-
"verifier_output": ti.verifier_output},
154-
"schema": _OutcomeIn, "formatter": self._fmt_outcome},
155-
{"name": "instructions", "raw_input": {}, "schema": _InstructionsIn,
156-
"formatter": lambda _d: tp.instructions_section(
157-
self._instructions_template, taxonomy_values)},
158-
{"name": "trajectory", "raw_input": {"trajectory": ti.trajectory},
159-
"schema": _TrajectoryIn, "formatter": self._fmt_trajectory},
153+
{
154+
"name": "preamble",
155+
"raw_input": {},
156+
"schema": _PreambleIn,
157+
"formatter": lambda _d: tp.PREAMBLE,
158+
},
159+
{
160+
"name": "task",
161+
"raw_input": {"task_name": ti.task_name, "instruction": ti.instruction},
162+
"schema": _TaskIn,
163+
"formatter": self._fmt_task,
164+
},
165+
{
166+
"name": "outcome",
167+
"raw_input": {
168+
"final_reward": ti.final_reward,
169+
"model_used": ti.model_used,
170+
"verifier_output": ti.verifier_output,
171+
},
172+
"schema": _OutcomeIn,
173+
"formatter": self._fmt_outcome,
174+
},
175+
{
176+
"name": "instructions",
177+
"raw_input": {},
178+
"schema": _InstructionsIn,
179+
"formatter": lambda _d: tp.instructions_section(
180+
self._instructions_template, taxonomy_values
181+
),
182+
},
183+
{
184+
"name": "trajectory",
185+
"raw_input": {"trajectory": ti.trajectory},
186+
"schema": _TrajectoryIn,
187+
"formatter": self._fmt_trajectory,
188+
},
160189
]
161190

162191
@staticmethod
163192
def _fmt_task(d: _TaskIn) -> str:
164-
instruction = _truncate(d.instruction) if d.instruction is not None else "[unavailable]"
193+
instruction = (
194+
_truncate(d.instruction) if d.instruction is not None else "[unavailable]"
195+
)
165196
return tp.task_section(d.task_name, instruction)
166197

167198
@staticmethod
168199
def _fmt_outcome(d: _OutcomeIn) -> str:
169200
reward = f"{d.final_reward}" if d.final_reward is not None else "[unavailable]"
170-
verifier = _truncate(d.verifier_output) if d.verifier_output is not None else "[unavailable]"
201+
verifier = (
202+
_truncate(d.verifier_output)
203+
if d.verifier_output is not None
204+
else "[unavailable]"
205+
)
171206
model = d.model_used or "[unavailable]"
172207
return tp.outcome_section(reward, verifier, model)
173208

174209
@staticmethod
175210
def _fmt_trajectory(d: _TrajectoryIn) -> str:
176211
from api.services.summarize_trajectory import preprocess
212+
177213
return tp.trajectory_section(json.dumps(preprocess(d.trajectory)))
178214

179215
# ---- parsing (parse is inherited; this filters elements) ----
@@ -187,10 +223,15 @@ def _valid_step_ids(self) -> set[int]:
187223
def filter_output(self, parsed: TrajectoryOutput) -> TrajectoryOutput:
188224
valid = self._valid_step_ids()
189225
highlights = [
190-
{"step_id": h["step_id"], "title": str(h.get("title") or "").strip(),
191-
"why": str(h.get("why") or "").strip()}
226+
{
227+
"step_id": h["step_id"],
228+
"title": str(h.get("title") or "").strip(),
229+
"why": str(h.get("why") or "").strip(),
230+
}
192231
for h in parsed.highlights
193-
if isinstance(h, dict) and isinstance(h.get("step_id"), int) and h["step_id"] in valid
232+
if isinstance(h, dict)
233+
and isinstance(h.get("step_id"), int)
234+
and h["step_id"] in valid
194235
]
195236
components: list[dict] = []
196237
for c in parsed.components:
@@ -202,11 +243,13 @@ def filter_output(self, parsed: TrajectoryOutput) -> TrajectoryOutput:
202243
ids = [s for s in m.step_ids if s in valid]
203244
if not ids:
204245
continue
205-
components.append({
206-
"step_ids": ids,
207-
"trajectory_component": m.trajectory_component.value,
208-
"summary": m.summary,
209-
})
246+
components.append(
247+
{
248+
"step_ids": ids,
249+
"trajectory_component": m.trajectory_component.value,
250+
"summary": m.summary,
251+
}
252+
)
210253
return TrajectoryOutput(
211254
summary=str(parsed.summary or "").strip(),
212255
highlights=highlights,
@@ -227,15 +270,22 @@ def timestamp_ms(step: dict) -> float | None:
227270
if not isinstance(value, str):
228271
return None
229272
try:
230-
return datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp() * 1000
273+
return (
274+
datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
275+
* 1000
276+
)
231277
except ValueError:
232278
return None
233279

234280
def duration_ms(index: int, step: dict) -> int:
235281
if index == 0:
236282
return 0
237283
current = timestamp_ms(step)
238-
previous = timestamp_ms(steps[index - 1]) if isinstance(steps[index - 1], dict) else None
284+
previous = (
285+
timestamp_ms(steps[index - 1])
286+
if isinstance(steps[index - 1], dict)
287+
else None
288+
)
239289
if current is None or previous is None:
240290
return 0
241291
return max(0, round(current - previous))
@@ -247,17 +297,21 @@ def duration_ms(index: int, step: dict) -> int:
247297
for step_id in component["step_ids"]
248298
if step_id in step_by_id
249299
]
250-
components.append({
251-
**component,
252-
# These fields are derived from the immutable trajectory rather
253-
# than supplied by the LLM, so consumers can safely aggregate them.
254-
"tool_count": sum(
255-
len(step.get("tool_calls") or [])
256-
for _, step in component_steps
257-
if isinstance(step.get("tool_calls"), list)
258-
),
259-
"duration_ms": sum(duration_ms(index, step) for index, step in component_steps),
260-
})
300+
components.append(
301+
{
302+
**component,
303+
# These fields are derived from the immutable trajectory rather
304+
# than supplied by the LLM, so consumers can safely aggregate them.
305+
"tool_count": sum(
306+
len(step.get("tool_calls") or [])
307+
for _, step in component_steps
308+
if isinstance(step.get("tool_calls"), list)
309+
),
310+
"duration_ms": sum(
311+
duration_ms(index, step) for index, step in component_steps
312+
),
313+
}
314+
)
261315
return {
262316
"schema_version": "5",
263317
"model": model,

frontend/src/app/globals.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,9 @@
215215
--tc-thinking-recall: #1baf7a;
216216
--tc-thinking-understand: #2a78d6;
217217
--tc-thinking-hypothesize: #a184ea;
218-
--tc-thinking-diagnose: #4a3aa7;
218+
--tc-thinking-correction: #4a3aa7;
219219
--tc-implementing: #eb6834;
220+
--tc-implementing-correction: #784000;
220221
--tc-writing-tests: #eda100;
221222
--tc-testing-public: #e87ba4;
222223
--tc-testing-custom: #c94592;
@@ -281,8 +282,9 @@
281282
--tc-thinking-recall: #199e70;
282283
--tc-thinking-understand: #3987e5;
283284
--tc-thinking-hypothesize: #9085e9;
284-
--tc-thinking-diagnose: #7a68d8;
285+
--tc-thinking-correction: #7a68d8;
285286
--tc-implementing: #d95926;
287+
--tc-implementing-correction: #b85850;
286288
--tc-writing-tests: #c98500;
287289
--tc-testing-public: #ce6b98;
288290
--tc-testing-custom: #d55181;

frontend/src/lib/trajectory-metrics.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@ const COMPONENT_COLOR_VARS: Record<string, string> = {
4949
thinking_recall: "var(--tc-thinking-recall)",
5050
thinking_understand: "var(--tc-thinking-understand)",
5151
thinking_hypothesize: "var(--tc-thinking-hypothesize)",
52-
thinking_diagnose: "var(--tc-thinking-diagnose)",
52+
thinking_correction: "var(--tc-thinking-correction)",
5353
implementing: "var(--tc-implementing)",
54+
implementing_correction: "var(--tc-implementing-correction)",
5455
writing_tests: "var(--tc-writing-tests)",
5556
testing_public: "var(--tc-testing-public)",
5657
testing_custom: "var(--tc-testing-custom)",
57-
testing_custom_edge_cases: "var(--tc-testing-edge)",
58+
testing_edge_cases: "var(--tc-testing-edge)",
5859
debugging: "var(--tc-debugging)",
60+
// Retired kinds, kept so stored summaries keep their fixed color rather than
61+
// falling through to appearance-order phase slots. Each shares the slot its
62+
// replacement took over; the two vocabularies never co-occur in one summary.
63+
thinking_diagnose: "var(--tc-thinking-correction)",
64+
testing_custom_edge_cases: "var(--tc-testing-edge)",
5965
// Synthetic bucket for steps no component claims — always neutral gray.
6066
other: "var(--phase-other)",
6167
};

frontend/src/lib/trajectory-segments.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ const COMPONENT_LABELS: Record<TrajectoryComponentKind, string> = {
1010
thinking_recall: "Recalling",
1111
thinking_understand: "Understanding",
1212
thinking_hypothesize: "Hypothesizing",
13-
thinking_diagnose: "Diagnosing",
13+
thinking_correction: "Correcting course",
1414
implementing: "Implementing",
15+
implementing_correction: "Correcting implementation",
1516
writing_tests: "Writing tests",
1617
testing_public: "Running public tests",
1718
testing_custom: "Running custom tests",
18-
testing_custom_edge_cases: "Testing edge cases",
19+
testing_edge_cases: "Testing edge cases",
1920
debugging: "Debugging",
21+
thinking_diagnose: "Diagnosing",
22+
testing_custom_edge_cases: "Testing edge cases",
2023
};
2124

2225
/** Display label for a taxonomy value; unknown values degrade to de-snaked text. */

frontend/src/lib/types.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -693,13 +693,17 @@ export type TrajectoryComponentKind =
693693
| "thinking_recall"
694694
| "thinking_understand"
695695
| "thinking_hypothesize"
696-
| "thinking_diagnose"
696+
| "thinking_correction"
697697
| "implementing"
698+
| "implementing_correction"
698699
| "writing_tests"
699700
| "testing_public"
700701
| "testing_custom"
701-
| "testing_custom_edge_cases"
702-
| "debugging";
702+
| "testing_edge_cases"
703+
| "debugging"
704+
// Retired from the backend enum, but stored summaries still carry them.
705+
| "thinking_diagnose"
706+
| "testing_custom_edge_cases";
703707

704708
export interface TrajectoryComponent {
705709
step_ids: number[];

0 commit comments

Comments
 (0)