Skip to content

Ft/syn agent bench - #43

Open
fatemetkl wants to merge 19 commits into
mainfrom
ft/syn_agent_bench
Open

Ft/syn agent bench#43
fatemetkl wants to merge 19 commits into
mainfrom
ft/syn_agent_bench

Conversation

@fatemetkl

@fatemetkl fatemetkl commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Clickup Ticket(s): Link(s) if applicable.
https://app.clickup.com/t/10524786/868kerrr8
https://app.clickup.com/t/10524786/868kerrd0

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • 🔧 Refactoring (no functional changes)
  • ⚡ Performance improvement
  • 🧪 Test improvements
  • 🔒 Security fix

Changes Made

  • Added the agent benchmark generation reference implementation
  • 4 notebooks are added. They cover task generation pipeline, quality checks, and verification, single-agent as well as multi-agent evaluation pipelines with scoring.
  • A retail domain is defined and used in the notebooks. In a future PR, we will include a new domain as well as instructions on adding a new domain.
  • More documentation and educational material will be included in the next PR.

Testing

  • Tests pass locally (uv run pytest tests/)
  • Type checking passes (uv run mypy <src_dir>)
  • Linting passes (uv run ruff check src_dir/)
  • Manual testing performed (describe below)
    Manual testing is performed by running notebooks and extensively inspecting the steps.

Manual testing details:

Screenshots/Recordings

Related Issues

Deployment Notes

Checklist

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Documentation updated (if applicable)
  • No sensitive information (API keys, credentials) exposed

Summary by CodeRabbit

  • New Features

    • Added SynBench tooling for generating, validating, running, and scoring synthetic agent-benchmark tasks.
    • Added single-agent and multi-agent evaluation workflows with tool execution, simulated customer dialogue, planning, and review.
    • Added mock retail benchmark data, policies, tools, task scenarios, and evaluation criteria.
    • Added notebook walkthroughs for setup, task generation, verification, and evaluation.
  • Documentation

    • Added comprehensive SynBench documentation covering setup, workflows, domains, tasks, and scoring.
  • Tests

    • Added broad coverage for generation, validation, tool execution, scoring, dialogue, and integration workflows.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added the SynBench benchmark system for domain loading, synthetic task generation, LLM-based agent execution, verification, scoring, metrics, notebook workflows, and mock-retail fixtures. Updated project dependencies, lint settings, tests, and review configuration.

Changes

SynBench benchmark system

Layer / File(s) Summary
Schemas and domain loading
aieng-synthetic-data/aieng/syn_data/synbench/schemas/*, .../domain/*, .../agents/session.py
Added typed task, tool, domain, generation, action, and verification models. Added domain loading and readiness validation.
Environment, verification, and scoring
.../environment/*, .../fsm/*, .../verification/*, .../evaluation/*
Added tool dispatch, replay, hashing, FSM validation, draft verification, trajectory scoring, and aggregate metrics.
LLM integration and task generation
.../llm/*, .../generation/*, implementations/agent_benchmark_generation/domains/mock_retail/*
Added OpenAI-compatible LLM access, tool/message parsing, constraint sampling, prompt construction, task generation, and mock-retail domain behavior.
Agent execution pipeline
.../agents/*
Added tool-calling loops, simulated dialogue, planner and critic roles, single-agent execution, and multi-agent orchestration.
Display and workflows
.../display.py, implementations/agent_benchmark_generation/*
Added notebook display helpers, benchmark data, generation and evaluation notebooks, documentation, and utilities.
Tests and configuration
aieng-synthetic-data/tests/synbench/*, pyproject.toml, .pre-commit-config.yaml, .coderabbit.yaml
Added unit and integration coverage. Updated dependencies, mypy and Ruff settings, smoke tests, and review configuration.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title relates to agent benchmarking but uses abbreviated, branch-like wording that does not clearly describe the primary changes. Replace it with a concise descriptive title, such as "Add agent benchmark generation and evaluation pipelines".
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description covers the feature, main changes, testing, and manual validation; optional related-issue and deployment sections remain empty.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (15)
implementations/agent_benchmark_generation/domains/mock_retail/policy.md-28-31 (1)

28-31: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

The refusal wording uses a different spelling from the stored order status.

This section instructs the agent to say canceled. The domain sets the order status to cancelled, as shown in notebook 2 output line 221. Agents commonly mirror the wording they observe in tool results, so this mismatch degrades the COMMUNICATE scoring signal. The same mismatch appears in implementations/agent_benchmark_generation/data/benchmarks/mock_retail/tasks.json.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/domains/mock_retail/policy.md`
around lines 28 - 31, Update the cancellation-refusal wording in the
Communication section to use “cancelled,” matching the stored order status and
the corresponding task benchmark wording. Keep the instruction concise and
preserve the requirement to mention that the order cannot be cancelled due to
its status.
implementations/agent_benchmark_generation/3-single_agent_evaluation.ipynb-210-212 (1)

210-212: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the model name and the tool index label.

Two documentation defects exist in this notebook:

  • Line 210 states the default model is gemini-2.0-flash. The stored output at line 225 shows gemini-3.1-flash-lite-preview. Notebook 4 line 234 repeats the same stale value.
  • Line 258 prints "first tool", but line 261 indexes [3], which is cancel_order. Notebook 4 line 260 labels the same index correctly as cancel_order.
✏️ Proposed fix
-        "print(\"\\nOpenAI-style tool schema (first tool):\")\n",
+        "print(\"\\nOpenAI-style tool schema (cancel_order):\")\n",

Also applies to: 258-261

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/3-single_agent_evaluation.ipynb`
around lines 210 - 212, Update the notebook documentation to state that
get_client() defaults to gemini-3.1-flash-lite-preview, including the
corresponding stale value in notebook 4. Correct the output label near the tool
inspection to identify index [3] as cancel_order instead of “first tool,”
matching the actual indexed tool and notebook 4.
implementations/agent_benchmark_generation/2-generate_and_verify_tasks.ipynb-493-497 (1)

493-497: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not hardcode the draft count in the summary message.

The message prints / 3 while drafts comes from run.run(n=3). If a reader changes n, the printed denominator becomes wrong.

♻️ Proposed fix
-        "print(f\" ({len(verified_tasks)} / 3) passed the verification pipeline.\")\n",
+        "print(f\" ({len(verified_tasks)} / {len(drafts)}) passed the verification pipeline.\")\n",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/2-generate_and_verify_tasks.ipynb`
around lines 493 - 497, Update the verification summary near run.verify_drafts
to derive the denominator from the requested draft count or the drafts
collection instead of hardcoding 3, so the message remains accurate when
run.run(n=...) changes.
implementations/agent_benchmark_generation/2-generate_and_verify_tasks.ipynb-531-532 (1)

531-532: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the grammar in the Step 6 markdown.

"It might worth running" is not grammatical.

✏️ Proposed fix
-        "Before running evaluation, we need to load the generated tasks. It might worth running verification again in case we are not sure if it was done before."
+        "Before running evaluation, we need to load the generated tasks. It is worth running verification again if you are not sure it was done before."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/2-generate_and_verify_tasks.ipynb`
around lines 531 - 532, Update the Step 6 markdown text to correct the grammar
in the phrase “It might worth running,” while preserving the existing meaning
and surrounding instructions.
implementations/agent_benchmark_generation/README.md-3-5 (1)

3-5: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the grammar in the intro and prerequisites.

Line 3 has subject-verb disagreement. Line 5 needs a hyphen and is missing a noun after "synbench specific".

✏️ Proposed fix
-These notebooks walks through **SynBench** end to end: loading a domain, generating synthetic benchmark tasks, verifying them, and evaluating tool-calling agents.
+These notebooks walk through **SynBench** end to end: loading a domain, generating synthetic benchmark tasks, verifying them, and evaluating tool-calling agents.
 
-**Prerequisites:** from the repo root, run `uv sync --dev --group synbench` to install synbench specific as well as dev dependencies, and start the first Jupyter notebook. Select the kernel and run the cells.
+**Prerequisites:** from the repo root, run `uv sync --dev --group synbench` to install the synbench-specific dependencies as well as the dev dependencies. Then start the first Jupyter notebook, select the kernel, and run the cells.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/README.md` around lines 3 - 5,
Correct the introductory sentence in the README so “These notebooks” uses the
plural verb “walk.” In the prerequisites sentence, hyphenate “SynBench-specific”
and add the missing noun “dependencies” after it.

Source: Linters/SAST tools

aieng-synthetic-data/aieng/syn_data/synbench/display.py-94-97 (1)

94-97: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard content against None.

show_task formats initial_message with or '—', which indicates the scenario text can be None or empty. show_message calls content.replace(...) without a guard, so a None value raises AttributeError in the notebook. Notebook 3 passes task.user_scenario.initial_message directly to show_message.

🛡️ Proposed guard
-def show_message(content: str, *, role: str = "user", title: str | None = None) -> None:
+def show_message(
+    content: str | None, *, role: str = "user", title: str | None = None
+) -> None:
     """Render a single chat message."""
     heading = title or f"{role.capitalize()} message"
-    _emit(f"### {heading}\n\n> {content.replace(chr(10), chr(10) + '> ')}")
+    text = content or "*(empty)*"
+    _emit(f"### {heading}\n\n> {text.replace(chr(10), chr(10) + '> ')}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/display.py` around lines 94 -
97, Update show_message to handle a None content value before calling replace,
rendering the same placeholder used by show_task for missing scenario text while
preserving normal formatting for non-empty content.
implementations/agent_benchmark_generation/4-multi_agent_pipeline_evaluation.ipynb-633-637 (1)

633-637: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the step numbering.

The notebook goes from "Step 4" to "Step 6". No "Step 5" exists.

✏️ Proposed fix
-    "## Step 6 — Batch metrics (pass@1)\n",
+    "## Step 5 — Batch metrics (pass@1)\n",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@implementations/agent_benchmark_generation/4-multi_agent_pipeline_evaluation.ipynb`
around lines 633 - 637, Update the Step 6 heading in the notebook’s batch
metrics section to use the correct sequential step number, Step 5, preserving
the existing section title and content.
implementations/agent_benchmark_generation/README.md-9-13 (1)

9-13: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the .env.example path in the README.

.env.example is committed at the repository root. From implementations/agent_benchmark_generation/, use cp ../../.env.example .env.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/README.md` around lines 9 - 13,
Update the setup command in the README to copy the repository-root .env.example
using the correct ../../.env.example relative path from
implementations/agent_benchmark_generation/, while preserving the existing
instruction to configure OPENAI_API_KEY.

Source: Linters/SAST tools

aieng-synthetic-data/aieng/syn_data/synbench/domain/loader.py-95-97 (1)

95-97: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle a top-level seed-task list before calling .get().

The fallback implies that tasks.seed.json can contain a top-level list. That form fails because seed_raw.get(...) runs before the type check. Branch on isinstance(seed_raw, list) first.

Proposed fix
-    seed_tasks = [Task.model_validate(t) for t in seed_raw.get("tasks", seed_raw)]
+    raw_tasks = seed_raw if isinstance(seed_raw, list) else seed_raw.get("tasks")
+    if not isinstance(raw_tasks, list):
+        raise DomainLoadError("tasks.seed.json must contain a task list")
+    seed_tasks = [Task.model_validate(task) for task in raw_tasks]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/domain/loader.py` around lines
95 - 97, Update the seed loading logic in the task loader to check whether
seed_raw is a list before calling .get(); use the list directly for top-level
list files, otherwise read the tasks key with the existing fallback for
mapping-shaped data, then validate each entry with Task.model_validate.
aieng-synthetic-data/aieng/syn_data/synbench/fsm/validator.py-38-38 (1)

38-38: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject actions that reference undeclared tools.

Line 38 maps every unknown tool name to "lookup". An undeclared action can therefore pass FSM validation when the expected step is a lookup. Raise FSMValidationError before constructing action_tags. Add a regression test for an unknown tool in a lookup path.

Proposed fix
     allow_write: bool = cfg.get("allow_write", True)
     tags = _tool_tags(domain)
 
-    action_tags = [tags.get(a.name, "lookup") for a in actions]
+    unknown_tools = [action.name for action in actions if action.name not in tags]
+    if unknown_tools:
+        raise FSMValidationError(f"Unknown tool: {unknown_tools[0]}")
+
+    action_tags = [tags[action.name] for action in actions]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/fsm/validator.py` at line 38,
Validate every action’s tool name against the declared tool tags before
constructing action_tags in the FSM validator, and raise FSMValidationError for
any undeclared tool instead of defaulting it to "lookup". Add a regression test
covering an unknown tool used in a lookup path.
aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py-61-69 (1)

61-69: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The duplicate check scans the whole transcript and can drop a legitimate repeated utterance.

The any(...) scan compares user_msg against every user message in the session. The stated intent is only to avoid re-seeding the first message. If a customer repeats an identical utterance later in a dialogue (for example Any update?), this drops the new turn. The LLM is then called with a transcript that does not contain the newest customer message.

Compare against the last user message only.

🐛 Proposed fix
-        if user_msg and not any(
-            m.get("role") == "user" and m.get("content") == user_msg
-            for m in session.messages
-        ):
+        last_user = next(
+            (
+                m.get("content")
+                for m in reversed(session.messages)
+                if m.get("role") == "user"
+            ),
+            None,
+        )
+        if user_msg and last_user != user_msg:
             session.messages.append({"role": "user", "content": user_msg})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py` around lines 61
- 69, Update the initial user-message seeding logic in the session setup around
initial_user_message and session.messages so it checks only the final user
message, rather than scanning the entire transcript. Append user_msg when the
latest user message does not already match it, preserving legitimate repeated
utterances in later turns.
aieng-synthetic-data/aieng/syn_data/synbench/agents/critic.py-41-43 (1)

41-43: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

startswith("APPROVE") misclassifies some rejections as approvals.

The prompt asks for exactly APPROVE, but LLM output is free-form. Text such as Approval is not possible: the agent skipped the refund check. or Approve? No — the reply omits the order ID. both start with APPROVE after upper(). The critic then accepts a bad draft and dialogue._run_executor_turn skips the retry.

Match the approval token exactly, or require it as a standalone first line.

🐛 Proposed fix
-        text = (response.content or "").strip()
-        approved = text.upper().startswith("APPROVE")
-        return approved, text
+        text = (response.content or "").strip()
+        first_line = text.splitlines()[0].strip().rstrip(".!").upper() if text else ""
+        approved = first_line == "APPROVE"
+        return approved, text
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/critic.py` around lines
41 - 43, Update the approval parsing in the critic response flow to avoid prefix
matches: accept only an exact APPROVE token or APPROVE as a standalone first
line, while treating explanatory or negated responses as rejections. Preserve
returning the stripped response text alongside the boolean result.
aieng-synthetic-data/aieng/syn_data/synbench/agents/single.py-45-46 (1)

45-46: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Remove the synthetic role_trace entry.

display.py renders role_trace as role names, and notebook output shows initial_user_message beside actual roles. Remove session.role_trace.append("initial_user_message") so both runners expose consistent role names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/single.py` around lines
45 - 46, Remove the synthetic role_trace append from the AgentSession setup in
the single-agent runner, leaving session.role_trace populated only by actual
roles so its output remains consistent with the other runners.
aieng-synthetic-data/aieng/syn_data/synbench/generation/sampler.py-111-116 (1)

111-116: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report misconfigured generation.yaml field names as ValueError.

Two field lookups trust generation.yaml without checking it against db.json:

  • Line 111: record[self.cfg.id_field] raises a bare KeyError when id_field does not exist on the primary record. The surrounding guards at Lines 98 and 106 raise descriptive ValueError, and the docstring at Lines 91-95 documents only ValueError.
  • Line 116: record.get(rel.via, "") silently substitutes an empty string when related.*.via names a missing field. That empty id then reaches the generation prompt and the oracle arguments as a valid-looking constraint.

The empty-string default is the more damaging case, because it produces plausible but unusable tasks instead of failing.

🛡️ Proposed fix to fail loudly on a bad field mapping
         record = self.rng.choice(records)
-        primary_id = str(record[self.cfg.id_field])
+        if self.cfg.id_field not in record:
+            raise ValueError(
+                f"generation.yaml id_field '{self.cfg.id_field}' is missing from "
+                f"db['{self.cfg.primary_collection}'] records"
+            )
+        primary_id = str(record[self.cfg.id_field])
 
         # IDs the LLM must reuse in the draft Task (tools + user message)
         entities: dict[str, str] = {self.cfg.id_field: primary_id}
         for key, rel in self.cfg.related.items():
-            entities[key] = str(record.get(rel.via, ""))
+            if rel.via not in record:
+                raise ValueError(
+                    f"generation.yaml related['{key}'].via '{rel.via}' is missing "
+                    f"from db['{self.cfg.primary_collection}'] records"
+                )
+            entities[key] = str(record[rel.via])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/generation/sampler.py` around
lines 111 - 116, Update the record field lookups in the sampler generation flow:
validate that cfg.id_field and every related rel.via field exist on the primary
record, raising descriptive ValueError exceptions consistent with the guards
around them. Replace the related-field record.get fallback so missing mappings
cannot become empty-string entity IDs, while preserving normal ID conversion for
valid fields.
aieng-synthetic-data/aieng/syn_data/synbench/generation/llm.py-14-24 (1)

14-24: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Enforce an object result from complete_json.

ChatClient.complete_json can return a scalar when the response is an array such as [1] or ["x"]. A scalar root also produces a generic conversion exception. Reject non-object results with a clear generation error before generator.py accesses data["id"] or data.get(...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/generation/llm.py` around lines
14 - 24, Update call_llm_json to validate the result from client.complete_json
before returning it, accepting only dictionary/object results and raising a
clear generation error for arrays or scalar roots. Preserve the existing
successful object return path so generator.py can safely use data["id"] and
data.get(...).
🧹 Nitpick comments (17)
aieng-synthetic-data/tests/synbench/test_schemas_action_compare.py (1)

14-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test action_fingerprint() with raw actions.

Lines 14-16 normalize both inputs before calling action_fingerprint(). The test will still pass if action_fingerprint() stops normalizing its input. Pass a and b directly to protect the public contract.

Proposed fix
-    assert action_fingerprint(normalize_action(a)) == action_fingerprint(
-        normalize_action(b)
-    )
+    assert action_fingerprint(a) == action_fingerprint(b)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/tests/synbench/test_schemas_action_compare.py` around
lines 14 - 16, Update the test assertion for action_fingerprint() to pass raw
actions a and b directly, removing both normalize_action() calls. Keep the
comparison focused on action_fingerprint() so the test verifies its public
normalization behavior.
implementations/agent_benchmark_generation/utils/utils.py (1)

4-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add the return type annotation.

The repository runs Ruff. A Path return annotation documents the contract for the notebook callers that build DOMAIN_PATH and OUT_DIR from this value.

♻️ Proposed refactor
-def get_root_directory():
+def get_root_directory() -> Path:
     """Get the root directory of the agent benchmark generation implementation."""
     return Path(__file__).resolve().parents[1]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/utils/utils.py` around lines 4 -
6, Update get_root_directory to add a Path return type annotation, preserving
its existing Path return value for callers constructing DOMAIN_PATH and OUT_DIR.
implementations/agent_benchmark_generation/README.md (1)

67-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language to the fenced block.

markdownlint reports MD040 for this block. Use text so the ASCII diagram renders without syntax highlighting. Remove the trailing blank line inside the fence as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/README.md` around lines 67 - 80,
Update the fenced ASCII diagram in the README to specify the text language for
Markdown syntax highlighting, and remove the extra blank line before the closing
fence.

Source: Linters/SAST tools

implementations/agent_benchmark_generation/1-check_access_to_model.ipynb (1)

28-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the proxy base URL configurable.

The base URL is hardcoded. Users on another OpenAI-compatible endpoint must edit the notebook. Read it from the environment with the current value as the default, so the same .env file controls both the key and the endpoint.

♻️ Proposed refactor
-    "client = OpenAI(base_url=\"https://proxy.vectorinstitute.ai/v1\", api_key=os.environ.get(\"OPENAI_API_KEY\"))\n",
+    "client = OpenAI(\n",
+    "    base_url=os.environ.get(\"OPENAI_BASE_URL\", \"https://proxy.vectorinstitute.ai/v1\"),\n",
+    "    api_key=os.environ.get(\"OPENAI_API_KEY\"),\n",
+    ")\n",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@implementations/agent_benchmark_generation/1-check_access_to_model.ipynb`
around lines 28 - 38, Update the OpenAI client initialization around OpenAI so
base_url is read from an environment variable, using the existing proxy URL as
the default; keep OPENAI_API_KEY environment-based so both endpoint and
credentials are controlled by the same .env configuration.
implementations/agent_benchmark_generation/data/benchmarks/mock_retail/tasks.json (1)

13-27: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

communicate_info is empty while reward_basis includes COMMUNICATE.

Tasks gen_64991457_0 (this block) declares COMMUNICATE in reward_basis but supplies no required phrases. The communicate term then passes unconditionally, so the reward reduces to the DB term alone. That makes the declared reward basis misleading for readers of the benchmark file.

Either populate communicate_info or omit COMMUNICATE from reward_basis for this task.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@implementations/agent_benchmark_generation/data/benchmarks/mock_retail/tasks.json`
around lines 13 - 27, Update the evaluation criteria for task gen_64991457_0 so
communicate_info is populated with the required communication phrases, or remove
COMMUNICATE from reward_basis; keep the reward basis consistent with the task’s
actual communication requirements.
aieng-synthetic-data/aieng/syn_data/synbench/agents/prompts.py (1)

23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Policy truncation is silent and uses magic numbers.

Three different hard-coded limits truncate domain.policy (4000, 2000, 1500 characters). If a domain policy exceeds the limit, the agent never sees the trailing rules, but verification/domain_checks.py still enforces the full policy. That produces unexplained score drops that look like model failures.

Name the limits as module constants and log or warn when truncation occurs.

♻️ Proposed change
+AGENT_POLICY_CHARS = 4000
+PLANNER_POLICY_CHARS = 2000
+CRITIC_POLICY_CHARS = 1500

Then reference the constants in each prompt builder instead of the inline literals.

Also applies to: 43-43, 59-59

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/prompts.py` at line 23,
Define named module-level constants for the three policy truncation limits,
replace the inline 4000, 2000, and 1500 values in each prompt builder with those
constants, and emit a warning or log whenever domain.policy is truncated. Update
the prompt-building code around the referenced policy slices while preserving
the existing limits and prompt behavior.
aieng-synthetic-data/tests/synbench/test_tool_loop.py (1)

36-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The assertions hard-code fixture contents and hide the real precondition.

Three couplings make this test brittle:

  1. domain.seed_tasks[1] selects a task by index. A reordering of domains/mock_retail/tasks.seed.json silently changes what is tested.
  2. Lines 40-41 hard-code get_order and cancel_order. The stub already replays task.evaluation_criteria.actions, so the loop can be checked against that list directly.
  3. Line 42 requires communicate_info to contain the substring cancel. If communicate_info is empty, the stub falls back to "Done." on line 22 and the assertion fails with no indication of the cause.

Derive the expectations from the task so a fixture change produces a clear failure.

♻️ Proposed change
 def test_tool_loop_collects_actions(mock_retail_path):
     """The loop records each dispatched tool call in order."""
     domain = load_domain(mock_retail_path)
     task = domain.seed_tasks[1]
+    expected = [a.name for a in task.evaluation_criteria.actions]
+    assert expected, "seed task must define oracle actions"
     client = _OracleStubClient(task)
     session = ToolCallingLoop(domain, client=client).run(task)
-    assert len(session.agent_actions) == 2
-    assert session.agent_actions[0].name == "get_order"
-    assert session.agent_actions[1].name == "cancel_order"
-    assert any("cancel" in m.lower() for m in session.agent_messages)
+    assert [a.name for a in session.agent_actions] == expected
+    assert session.agent_messages
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/tests/synbench/test_tool_loop.py` around lines 36 - 42,
Update the test around ToolCallingLoop.run to select the seed task by an
explicit identifying property rather than index, derive expected action names
from task.evaluation_criteria.actions instead of hard-coded
get_order/cancel_order values, and assert the communication expectation from the
task’s configured criteria while making an empty communicate_info case explicit.
Keep the assertions aligned with the task data so fixture changes fail clearly.
aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py (2)

17-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

roles or DEFAULT_ROLES shares a module-level mutable list and treats [] as unset.

Two effects:

  1. When the caller passes no roles, self.roles is bound to the module-level DEFAULT_ROLES object. Any in-place mutation of pipeline.roles changes the default for every later AgentPipeline.
  2. roles=[] is falsy, so an explicit empty role list silently enables all four roles. run_user_dialogue in agents/dialogue.py has the same behavior on line 42.
♻️ Proposed change
-DEFAULT_ROLES = ["user_sim", "planner", "executor", "critic"]
+DEFAULT_ROLES: tuple[str, ...] = ("user_sim", "planner", "executor", "critic")
-        self.roles = roles or DEFAULT_ROLES
+        self.roles = list(DEFAULT_ROLES) if roles is None else list(roles)

Also applies to: 32-32

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py` at line 17,
Update AgentPipeline role initialization to distinguish roles=None from an
explicitly empty list and copy DEFAULT_ROLES when defaults are selected,
preventing instance mutations from changing the module-level list. Apply the
same None-aware, non-shared handling in run_user_dialogue.

17-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

roles or <default> treats an explicit empty list as unset in both role-selection sites. Both files use truthiness to detect a missing roles argument. A caller that passes roles=[] to disable all roles instead gets the full four-role default, and pipeline.py additionally binds the shared module-level DEFAULT_ROLES list. Test only for None.

  • aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py#L17-L32: make DEFAULT_ROLES a tuple and set self.roles = list(DEFAULT_ROLES) if roles is None else list(roles).
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/dialogue.py#L42-L42: change to active = ["user_sim", "planner", "executor", "critic"] if roles is None else roles.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py` around lines
17 - 32, Preserve an explicitly empty roles list instead of falling back to
defaults: in
aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py#L17-L32, make
DEFAULT_ROLES a tuple and update AgentPipeline.__init__ to copy DEFAULT_ROLES
when roles is None, otherwise copy the supplied roles; in
aieng-synthetic-data/aieng/syn_data/synbench/agents/dialogue.py#L42-L42, update
the active-role selection to check roles is None rather than truthiness,
preserving [] as no active roles.
aieng-synthetic-data/aieng/syn_data/synbench/agents/planner.py (1)

24-29: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The planner history drops user turns and can break role alternation.

Two concerns in this loop:

  1. Only assistant messages are forwarded. Earlier customer turns are dropped, so the planner cannot see identifiers or constraints the customer already gave. It sees agent replies plus the newest user message only.
  2. The result can contain several consecutive assistant entries, and the first entry after system is an assistant message. UserSimulator._merge_consecutive in agents/user_sim.py exists precisely because some providers reject that shape. The planner has no equivalent handling.

Forward both user and assistant turns in order, and skip assistant messages that carry tool_calls, matching the filtering in agents/user_sim.py.

Also fix the comment typo: hostoryhistory.

♻️ Proposed change
-        # First append the hostory
-        for m in session.messages:
-            if m.get("role") == "assistant" and m.get("content"):
-                messages.append({"role": "assistant", "content": m["content"]})
-        # Then append the user message
+        # First append the history, keeping both sides of the conversation.
+        for m in session.messages:
+            role = m.get("role")
+            content = (m.get("content") or "").strip()
+            if not content or role not in ("user", "assistant"):
+                continue
+            if role == "assistant" and m.get("tool_calls"):
+                continue
+            messages.append({"role": role, "content": content})
+        # Then append the current user message.
         messages.append({"role": "user", "content": user_message})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/planner.py` around lines
24 - 29, Update the history-building loop in the planner to preserve both user
and assistant messages in their original order, excluding assistant messages
that contain tool_calls, consistent with UserSimulator filtering. Ensure the
resulting messages maintain valid role alternation by applying the existing
consecutive-message merge approach or equivalent before appending the new user
message, and correct the comment typo from “hostory” to “history”.
aieng-synthetic-data/tests/synbench/test_user_sim_perspective.py (1)

102-108: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The prefix test depends on an unstated fixture value.

UserSimulator._clean strips Customer, User, and session.task.user_scenario.user_name. This test passes only when domain.seed_tasks[0].user_scenario.user_name equals Alice Chen. If domains/mock_retail/tasks.seed.json uses a different name, _clean leaves the prefix in place and the assertion fails without naming the cause.

Build the reply from the task so the test states its own precondition.

♻️ Proposed change
 def test_speaker_prefix_is_stripped(mock_retail_path):
     """A leading speaker name is removed from the simulator's reply."""
     domain = load_domain(mock_retail_path)
     session = _session(domain)
-    client = _CaptureClient(reply="Alice Chen: it is ord_1001.")
+    user_name = session.task.user_scenario.user_name
+    assert user_name, "seed task must define user_name"
+    client = _CaptureClient(reply=f"{user_name}: it is ord_1001.")
 
     assert UserSimulator(client).respond(session) == "it is ord_1001."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/tests/synbench/test_user_sim_perspective.py` around
lines 102 - 108, Update test_speaker_prefix_is_stripped to derive the speaker
name from the loaded task’s user_scenario.user_name, use that value when
constructing the captured reply, and retain the assertion for the stripped
response so the test explicitly matches UserSimulator._clean behavior without
relying on a hardcoded fixture name.
aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py (2)

100-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tool dispatch failures are swallowed without a log.

The bare except Exception converts every dispatch failure into a JSON error string in the transcript. That is reasonable for agent sandboxing, because the model should see the error and recover. However, nothing records the failure outside the transcript. A schema mistake in domains/mock_retail/tools.py or a bug in environment/core.py presents itself as an agent-side quality problem.

Add a debug or warning log next to the handler and keep the transcript payload unchanged.

Note: the json.dumps static analysis hints on lines 86, 101, and 103 do not apply. These strings are OpenAI tool-call payloads, not HTTP responses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py` around lines 100
- 104, Update the dispatch exception handler in the agent loop around
env.dispatch to emit a debug or warning log containing the caught exception,
while preserving the existing JSON error string in result_str unchanged. Do not
alter the tool-call payload serialization.

Source: Linters/SAST tools


71-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Turn-budget exhaustion ends the loop with no signal.

If the model requests tool calls on every one of max_turns iterations, the loop exits through the continue path. No final assistant text is recorded, and session.agent_messages stays empty. score_trajectory in evaluation/scoring.py then evaluates a trajectory that has no communication output, which is indistinguishable from a model that simply refused to answer.

The same silence applies when a response carries neither tool_calls nor content: line 122 breaks without recording anything.

Record the termination reason on the session, or emit a warning log, so evaluation runs can separate a budget overrun from a genuine agent failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py` around lines 71
- 122, Add explicit termination signaling to the loop around the response
handling in the agent method: record whether the loop ended because max_turns
was exhausted after repeated tool calls, and record a distinct reason when a
response has neither tool_calls nor content. Store the reason on the session
using its existing metadata/state mechanism, or emit a warning through the
established logger, so score_trajectory can distinguish budget exhaustion and
empty responses from genuine refusal.
aieng-synthetic-data/aieng/syn_data/synbench/agents/llm_agent.py (1)

10-12: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Use model_dump(mode="json") for serialization safety.

Action.arguments accepts arbitrary values. Python-mode output can cause json.dumps to raise TypeError for values such as datetime, Decimal, or UUID.

♻️ Proposed change
-    return json.dumps([a.model_dump() for a in actions])
+    return json.dumps([a.model_dump(mode="json") for a in actions])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/agents/llm_agent.py` around
lines 10 - 12, Update actions_to_json to call Action.model_dump with mode="json"
before passing the results to json.dumps, ensuring arbitrary Action.arguments
values such as datetime, Decimal, and UUID are converted into JSON-safe
representations.

Source: Linters/SAST tools

aieng-synthetic-data/aieng/syn_data/synbench/generation/prompt.py (2)

41-45: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Derive write_rule from the FSM tags, not the path length.

n > 1 is a proxy for "the path contains a write step". A task type with allow_write: true and a single-element path takes the else branch and receives "do not call write tools", which contradicts its own config.

No current mock_retail task type hits this combination, so this is a robustness improvement rather than an active defect. Checking the path tags removes the coupling to path length.

♻️ Proposed refactor
+    write_tags = {
+        t.fsm_tag for t in domain.tools if t.tool_type == ToolType.WRITE
+    }
     write_rule = (
         "Include exactly one write tool as the final action."
-        if allow_write and n > 1
+        if allow_write and any(tag in write_tags for tag in path)
         else "Use read-only tools only; do not call write tools."
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/generation/prompt.py` around
lines 41 - 45, Derive write_rule from the FSM path tags rather than the path
length: update the logic near write_rule to detect whether the path contains a
write step, while still requiring allow_write. Ensure single-element paths with
a write tag receive the final-write instruction, and read-only paths retain the
no-write instruction.

203-212: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make policy truncation explicit.

When a domain policy exceeds 1,500 characters, the prompt silently drops the remainder. Add a named limit with a comment that explains the prompt-budget reason, and append a truncation marker when the policy is incomplete.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/generation/prompt.py` around
lines 203 - 212, Update the prompt construction in the generation function
containing the domain policy to define a named 1,500-character policy limit with
a comment explaining the prompt-budget constraint. Detect policies exceeding
that limit, truncate them, and append a clear truncation marker; leave shorter
policies unchanged while preserving the existing prompt structure.
aieng-synthetic-data/aieng/syn_data/synbench/generation/generator.py (1)

58-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate the verification post-processing.

run_and_verify (Lines 60-64) and verify_drafts (Lines 70-73) contain identical filter_verified handling. Let run_and_verify delegate to verify_drafts.

♻️ Proposed refactor
     def run_and_verify(self, n: int) -> tuple[list[Task], list[tuple[Task, list[str]]]]:
         """Generate ``n`` drafts and split them into verified tasks and rejections."""
-        drafts = self.run(n)
-        verified, rejected_drafts, _ = filter_verified(self.domain, drafts)
-        rejections = [(r.task, errors) for r, errors in rejected_drafts]
-        verified_tasks = [v.task for v in verified]
-        return verified_tasks, rejections
+        return self.verify_drafts(self.run(n))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aieng-synthetic-data/aieng/syn_data/synbench/generation/generator.py` around
lines 58 - 73, Deduplicate the verification handling by updating run_and_verify
to generate drafts with run(n) and return the result of verify_drafts(drafts).
Keep the existing filter_verified processing and return behavior centralized in
verify_drafts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 078e75bb-d93d-45b7-adca-ed47d5ce9dd1

📥 Commits

Reviewing files that changed from the base of the PR and between 6ae91c9 and 44c2d98.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (96)
  • .coderabbit.yaml
  • .pre-commit-config.yaml
  • aieng-synthetic-data/aieng/syn_data/image/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/critic.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/dialogue.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/llm_agent.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/loop.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/pipeline.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/planner.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/prompts.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/session.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/single.py
  • aieng-synthetic-data/aieng/syn_data/synbench/agents/user_sim.py
  • aieng-synthetic-data/aieng/syn_data/synbench/display.py
  • aieng-synthetic-data/aieng/syn_data/synbench/domain/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/domain/loader.py
  • aieng-synthetic-data/aieng/syn_data/synbench/environment/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/environment/communicate.py
  • aieng-synthetic-data/aieng/syn_data/synbench/environment/core.py
  • aieng-synthetic-data/aieng/syn_data/synbench/environment/hashing.py
  • aieng-synthetic-data/aieng/syn_data/synbench/evaluation/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/evaluation/metrics.py
  • aieng-synthetic-data/aieng/syn_data/synbench/evaluation/scoring.py
  • aieng-synthetic-data/aieng/syn_data/synbench/fsm/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/fsm/validator.py
  • aieng-synthetic-data/aieng/syn_data/synbench/generation/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/generation/generator.py
  • aieng-synthetic-data/aieng/syn_data/synbench/generation/llm.py
  • aieng-synthetic-data/aieng/syn_data/synbench/generation/prompt.py
  • aieng-synthetic-data/aieng/syn_data/synbench/generation/sampler.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/chat_client.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/client.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/config.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/parsing.py
  • aieng-synthetic-data/aieng/syn_data/synbench/llm/tools.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/actions.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/domain.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/generation.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/tasks.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/tools.py
  • aieng-synthetic-data/aieng/syn_data/synbench/schemas/verification.py
  • aieng-synthetic-data/aieng/syn_data/synbench/verification/__init__.py
  • aieng-synthetic-data/aieng/syn_data/synbench/verification/domain_checks.py
  • aieng-synthetic-data/aieng/syn_data/synbench/verification/errors.py
  • aieng-synthetic-data/aieng/syn_data/synbench/verification/pipeline.py
  • aieng-synthetic-data/pyproject.toml
  • aieng-synthetic-data/tests/synbench/__init__.py
  • aieng-synthetic-data/tests/synbench/conftest.py
  • aieng-synthetic-data/tests/synbench/fixtures/llm_responses/draft_cancel.json
  • aieng-synthetic-data/tests/synbench/fixtures/tasks_invalid_fsm.json
  • aieng-synthetic-data/tests/synbench/fixtures/tasks_invalid_replay.json
  • aieng-synthetic-data/tests/synbench/fixtures/tasks_valid.json
  • aieng-synthetic-data/tests/synbench/integration/__init__.py
  • aieng-synthetic-data/tests/synbench/integration/conftest.py
  • aieng-synthetic-data/tests/synbench/integration/test_agentic_pipeline.py
  • aieng-synthetic-data/tests/synbench/integration/test_call_llm_json.py
  • aieng-synthetic-data/tests/synbench/integration/test_generation_run.py
  • aieng-synthetic-data/tests/synbench/test_environment_tool_dispatch.py
  • aieng-synthetic-data/tests/synbench/test_evaluation_metrics.py
  • aieng-synthetic-data/tests/synbench/test_fsm_invalid_path.py
  • aieng-synthetic-data/tests/synbench/test_fsm_valid_path.py
  • aieng-synthetic-data/tests/synbench/test_generation_config.py
  • aieng-synthetic-data/tests/synbench/test_generation_dedup.py
  • aieng-synthetic-data/tests/synbench/test_prompt_builder_snapshot.py
  • aieng-synthetic-data/tests/synbench/test_schemas_action_compare.py
  • aieng-synthetic-data/tests/synbench/test_schemas_task_roundtrip.py
  • aieng-synthetic-data/tests/synbench/test_tool_loop.py
  • aieng-synthetic-data/tests/synbench/test_user_sim_perspective.py
  • aieng-synthetic-data/tests/synbench/test_verifier_policy_rules.py
  • aieng-synthetic-data/tests/synbench/test_verifier_replay_failure.py
  • aieng-synthetic-data/tests/synbench/test_verifier_replay_success.py
  • aieng-synthetic-data/tests/test_smoke.py
  • implementations/agent_benchmark_generation/1-check_access_to_model.ipynb
  • implementations/agent_benchmark_generation/2-generate_and_verify_tasks.ipynb
  • implementations/agent_benchmark_generation/3-single_agent_evaluation.ipynb
  • implementations/agent_benchmark_generation/4-multi_agent_pipeline_evaluation.ipynb
  • implementations/agent_benchmark_generation/README.md
  • implementations/agent_benchmark_generation/__init__.py
  • implementations/agent_benchmark_generation/data/benchmarks/mock_retail/tasks.json
  • implementations/agent_benchmark_generation/domains/mock_retail/db.json
  • implementations/agent_benchmark_generation/domains/mock_retail/generation.yaml
  • implementations/agent_benchmark_generation/domains/mock_retail/policy.md
  • implementations/agent_benchmark_generation/domains/mock_retail/state_machine.yaml
  • implementations/agent_benchmark_generation/domains/mock_retail/tasks.seed.json
  • implementations/agent_benchmark_generation/domains/mock_retail/tools.py
  • implementations/agent_benchmark_generation/domains/mock_retail/user_simulator.yaml
  • implementations/agent_benchmark_generation/domains/mock_retail/verify.py
  • implementations/agent_benchmark_generation/utils/__init__.py
  • implementations/agent_benchmark_generation/utils/utils.py
  • implementations/implementation_b/README.md
  • implementations/implementation_b/topic_b_a.ipynb
  • pyproject.toml
💤 Files with no reviewable changes (4)
  • aieng-synthetic-data/aieng/syn_data/image/init.py
  • implementations/implementation_b/topic_b_a.ipynb
  • aieng-synthetic-data/tests/test_smoke.py
  • implementations/implementation_b/README.md

Comment thread aieng-synthetic-data/aieng/syn_data/synbench/agents/dialogue.py
Comment thread aieng-synthetic-data/aieng/syn_data/synbench/domain/loader.py
Comment thread aieng-synthetic-data/aieng/syn_data/synbench/environment/core.py
Comment thread aieng-synthetic-data/aieng/syn_data/synbench/environment/core.py
Comment thread implementations/agent_benchmark_generation/domains/mock_retail/tools.py Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cell #1 I get errors for from utils.utils import get_root_directory.
The kernel is running from the repo root, not from implementations/agent_benchmark_generation, so from utils.utils import ... fails.
The notebook assumes cwd = its own directory, but Cursor/VS Code (and many other runners) default to the workspace root.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see what you mean. My Jupyter kernel is also set to the default .venv/bin/activate. The notebooks also run on Coder with no errors, so that's why I assumed it should be fine. Let's meet quickly whenever you are available so that I can find out what I am doing differently.

I also just looked into another reference implementation to see how they handle utils. It seems they are doing a similar thing and import utils in notebooks ( Example: from utils.dataset_helpers), but I might definitely be missing something, so let's meet.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use rich to format cell print outputs (tables, panels, etc), they help a lot in readability. E.g. in step 4 I see a sample json but it's a bit hard to figure out what is what.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are Run and Draft jargons defined in τ-bench? Can you provide a brief explanation about them?
I think having a workflow diagram of the steps would help clarify what the notebooks do. Maybe we could have one for the overall notebooks workflow, and then a separate diagram for each notebook if it makes sense.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that when we change ROOT to Implementation root: /../synthetic-data-bootcamp  step 6 fails here:
assert errors == [], f"Domain validation failed: {errors}"
So I think we should update cell 2 like this:

DOMAIN_PATH = ROOT / "implementations" / "agent_benchmark_generation" / "domains" / "mock_retail"
OUT_DIR = ROOT / "implementations" / "agent_benchmark_generation" / "data" / "benchmarks" / "mock_retail"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: task.json is not added to gitignore.

@rjavadi rjavadi Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In notebook 4 - step 2:
task_path = OUT_DIR / "tasks.json"
if it's the same file we generated and used in previous notebooks, it's under ROOT / "data" / "benchmarks" / "mock_retail" and current value of OUT_DIR results in file not found error because "notebook_walkthrough" is empty.

Comment thread pyproject.toml
@@ -1,5 +1,5 @@
[project]
name = "aieng-synthetic-data"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this according to the repo level name

]

[project]
name = "aieng-topic-impl"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set this to the package name

Comment thread .coderabbit.yaml

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Configurations for an improved coderabbit setup

python-version-file: ".python-version"

- name: Install dependencies and check code
# Install dependencies for all the necessary groups

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: remove this comment

uv sync --all-extras --dev
uv run --directory aieng-synthetic-data pytest -m "not integration_test" tests
uv run --directory aieng-synthetic-data pytest -m "not integration_test" --ignore=tests/synbench tests
# Run synbench tests if there are changes in the synbench package

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this job to run synbench tests only when there are changes under 'aieng-synthetic-data/aieng/synbench/**
Otherwise, synbench tests won't run.

drafts: list[Task] = []
for i in range(n):
tid = f"gen_{uuid4().hex[:8]}_{i}"
drafts.append(self.generator.generate_one(task_id=tid))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For follow-up PRs and speed-up:

We can call some of the generations simultaneously with multi-threading and semaphores to speed up this part.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants