Skip to content

feat(guidelines): consistency-based guideline generation#289

Open
evduester wants to merge 64 commits into
mainfrom
feat/consistency-guidelines
Open

feat(guidelines): consistency-based guideline generation#289
evduester wants to merge 64 commits into
mainfrom
feat/consistency-guidelines

Conversation

@evduester

@evduester evduester commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a new consistency-based guideline generation pipeline alongside the existing regular pipeline, controlled by a single EVOLVE_GUIDELINES_MODE env var.

  • Vendor consistency analyzer — copies 8 modules from the IBM-internal agent-consistency package into altk_evolve/llm/guidelines/consistency_analyzer/, replacing the pip path dependency. Includes a custom inference_utils.py LiteLLM adapter and strips dead code (predictive entropy, CUGA paths, embedding kernels).
  • generate_consistency_guidelines() — new pipeline: transforms a raw trajectory to IR, resamples each step N times, scores consistency per step, then generates guidelines focused on the highest-uncertainty steps.
  • EVOLVE_GUIDELINES_MODE env var (regular | consistency | both) — replaces the --consistency CLI flag and the model parameter on the save_trajectory MCP tool. Both the Phoenix sync and MCP paths respect it; model selection always falls back to llm_settings.guidelines_model.
  • EVOLVE_DEBUG_DIR env var — when set, writes 5 debug artifacts per trajectory: raw trajectory, IR with consistency scores, consistency score card, consistency guidelines, regular guidelines.
  • Bug fixes in vendored codeNumericFractionConsistencyMetric._get_distance pairwise loop (samples[j]samples[i+1+j]); resampling model fallback blocked by literal "unknown" string; segmentation guard (n_steps >= 2) preventing LLM hallucinating subtasks on single-step trajectories.
  • generation_method metadata — all auto-generated guidelines now carry "regular" or "consistency" in their metadata.

Test plan

  • uv run pytest tests/unit/ --ignore=tests/unit/test_milvus_backend.py -q — 487 unit tests pass (includes 114 new tests for vendored consistency_analyzer modules, segmentation guard tests, updated CLI/MCP/phoenix_sync tests)
  • uv run --env-file .env pytest tests/e2e/test_e2e_consistency_pipeline.py --run-e2e -v -s — consistency pipeline e2e (openai_agents + smolagents via Phoenix)
  • uv run --env-file .env pytest tests/e2e/test_e2e_consistency_pipeline.py::test_e2e_both_mode_smolagents --run-e2e -v -s — both-mode e2e (regular + consistency in one sync pass)
  • uv run --env-file .env pytest tests/e2e/test_e2e_mcp_consistency.py --run-e2e -v -s — consistency via MCP save_trajectory
  • uv run --env-file .env pytest tests/e2e/test_e2e_smolagent_mcp.py --run-e2e -v -s — smolagents MCP path

Summary by CodeRabbit

  • New Features
    • Added consistency-based guideline generation with selectable regular, consistency, or both modes (including MCP save_trajectory and Phoenix sync tagging with generation_method).
    • Added Litellm-based trajectory resampling, response parsing, consistency scoring, and score-card/uncertainty output.
    • Added a --guidelines-mode option to the Phoenix sync CLI.
  • Documentation / Config
    • Extended environment and packaging to support guideline/debug options; debug directory artifact support.
  • Tests
    • Added extensive unit and end-to-end coverage for consistency and both-mode flows (including MCP and Phoenix sync).

evduester and others added 8 commits May 5, 2026 11:33
Adds `generate_consistency_guidelines()` ported from the pre-sync
`kaizen/llm/tips/consistency_tips.py` onto the new `altk_evolve` layout.
Uses `agent-consistency` (optional extra) to resample the trajectory,
score per-step uncertainty, and focus the LLM prompt on the highest-
uncertainty steps. Returns `list[GuidelineGenerationResult]` to match
the shape of `generate_guidelines()`.

The `agent-consistency` package is wired as an editable source at
`../agent-consistency`; install with `uv sync --extra consistency`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`PhoenixSync(use_consistency_guidelines=True)` and `evolve sync phoenix
--consistency` route trajectory processing through the consistency
guideline generator instead of the default `generate_guidelines()`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updates the demo shell script and README to reflect the upstream
rename of `kaizen` -> `altk_evolve`:
- `KAIZEN_*` env vars -> `EVOLVE_*`
- `kaizen` CLI -> `evolve`
- `python -m kaizen.cli.cli` -> `evolve`
- `KaizenClient` -> `EvolveClient`
- `extract_trajectories.py` -> `scripts/extract_trajectories.py`
- Replaces hard-coded `/Users/duester/Work/kaizen` with a placeholder
- Matches new `generated_guidelines_*` output filename from the script

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…r, guidelines_mode, auto-mcp support

- Vendor consistency_analyzer package (9 files) replacing IBM-internal agent-consistency path dep
- Replace inference_utils.py with LiteLLM adapter; strip dead code (predictive entropy, kernels, CUGA paths)
- Add generate_consistency_guidelines() pipeline: transform IR → resample → score → generate
- Add guidelines_mode param ("regular"|"consistency"|"both") to PhoenixSync and save_trajectory MCP tool
- Replace --consistency CLI flag with --guidelines-mode [regular|consistency|both]
- Add generation_method metadata field to all auto-generated guidelines
- Add model fallback (llm_settings.guidelines_model) when trajectory carries no model info
- Unit tests: _process_trajectory dispatch, CLI flag, MCP params, IR transformation, pure functions
- E2E tests: consistency pipeline (openai_agents + smolagents), auto-mcp consistency mode
- Integration design doc: docs/consistency_guidelines_integration.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves 3 conflicts between the guidelines_mode refactoring and the
phoenix span-extraction fix (PR #273) that was merged into main:
- phoenix_sync.py: keep origin/main's span_kind variable form in _is_llm_span
- mcp_server.py: switch trajectory persistence to _persist_entities() helper
- test_phoenix_sync.py: retain new TestProcessTrajectoryGuidelinesMode class

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pipeline

- Move agent_config.yaml into consistency_analyzer/ (correct home; code already referenced it there)
- Fix NumericFractionConsistencyMetric._get_distance: samples[j] → samples[i+1+j] (pairwise loop bug)
- Fix resampling model fallback: trajectory.get("model") or None, avoiding "unknown" blocking fallback
- Guard segmentation behind n_steps >= 2 to prevent LLM hallucinating subtasks on single-step trajectories
- Remove model param from save_trajectory MCP tool (always use llm_settings.guidelines_model fallback)
- Remove success_probability dead code from consistency guideline generation
- Reduce debug artifacts: keep trajectory_*.json, trajectory_ir_*_cns.json, consistency_score_card_*.json, guidelines_*_consistency.json
- Add guidelines_*_regular.json debug artifact for regular pipeline (phoenix_sync.py)
- Add EVOLVE_GUIDELINES_MODE and EVOLVE_DEBUG_DIR to .env.example
- Update docs for EVOLVE_GUIDELINES_MODE env var and removed MCP model param
- Unit tests: 114 tests for vendored consistency_analyzer modules; TestSegmentationGuard; updated for renames
- E2E tests: smolagents MCP test; both-mode smolagents+Phoenix test; always write to consistency_debug/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a consistency analyzer and consistency-based guideline generation pipeline selectable through regular, consistency, or both modes. Integrates it with MCP and Phoenix sync, adds configuration and packaging support, and introduces unit and E2E coverage.

Changes

Consistency guidelines feature

Layer / File(s) Summary
Configuration and packaging
.env.example, .gitignore, pyproject.toml, MANIFEST.in, altk_evolve/config/guidelines.py, .../agent_config.yaml
Adds guideline/debug settings, analyzer configuration, YAML packaging, tooling configuration, and ignored debug output.
Consistency analyzer core
altk_evolve/llm/guidelines/consistency_analyzer/*
Adds response parsing, trajectory resampling, LiteLLM sampling, consistency metrics, aggregation, field utilities, step scoring, and public exports.
Consistency guideline generation
altk_evolve/llm/guidelines/consistency_guidelines.py, .../prompts/generate_consistency_guidelines.jinja2
Transforms trajectories, computes uncertainty, optionally segments subtasks, generates structured guidelines, and writes debug artifacts.
CLI, MCP, and Phoenix integration
altk_evolve/cli/cli.py, altk_evolve/frontend/mcp/mcp_server.py, altk_evolve/sync/phoenix_sync.py
Adds mode selection, dispatches regular and consistency pipelines, tags generated entities, passes MCP tools, and batches persistence.
E2E infrastructure and validation
tests/e2e/*, tests/unit/*
Adds Phoenix lifecycle handling and tests analyzer behavior, guideline generation, mode dispatch, metadata, and persistence.

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

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant MCPOrPhoenixSync
  participant ConsistencyGuidelines
  participant ConsistencyAnalyzer
  participant EvolveBackend
  Agent->>MCPOrPhoenixSync: submit trajectory
  MCPOrPhoenixSync->>ConsistencyGuidelines: generate consistency guidelines
  ConsistencyGuidelines->>ConsistencyAnalyzer: resample and analyze trajectory
  ConsistencyAnalyzer-->>ConsistencyGuidelines: score card and uncertainty data
  ConsistencyGuidelines-->>MCPOrPhoenixSync: guideline results
  MCPOrPhoenixSync->>EvolveBackend: persist tagged guideline entities
Loading

Possibly related PRs

Suggested reviewers: jayaramkr, gaodan-fang, visahak, vinodmut

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding consistency-based guideline generation.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/consistency-guidelines

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.

evduester and others added 3 commits July 7, 2026 21:14
…d consistency_analyzer

- Auto-format 20 files (consistency_analyzer modules, consistency_guidelines, phoenix_sync, tests)
- Add per-file ruff ignores for consistency_analyzer/: E402/E722/F841 are upstream issues in vendored code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add types-PyYAML dev dependency (yaml import was untyped)
- Add mypy ignore_errors override for vendored consistency_analyzer (upstream type issues)
- Remove class-scoped import in test_consistency_analyzer.py (mypy misc error)
- Add yaml to ignore_missing_imports override (belt-and-suspenders)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@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: 14

Note

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

🟡 Minor comments (11)
tests/e2e/test_e2e_consistency_pipeline.py-172-204 (1)

172-204: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Blocking readline() can hang the test indefinitely.

The timeout check runs before readline(), but readline() itself blocks until a line is available or the pipe closes. If the sync process hangs without producing output, readline() blocks forever — the finally block with process.terminate() is never reached, leaking the subprocess and hanging the test.

🔧 Proposed fix using `select` to avoid blocking
+import select
+
     try:
         while True:
             if time.time() - sync_start > timeout:
                 print(f"Timeout waiting for consistency sync ({timeout}s)")
                 break

+            # Check if data is available before blocking on readline()
+            ready, _, _ = select.select([process.stdout], [], [], 0.5)
+            if not ready:
+                if process.poll() is not None:
+                    break
+                continue
+
             line = process.stdout.readline()
             if not line:
                 if process.poll() is not None:
                     break
                 time.sleep(0.1)
                 continue

Also applies to: 348-377

🤖 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 `@tests/e2e/test_e2e_consistency_pipeline.py` around lines 172 - 204, The
consistency-sync polling loop uses process.stdout.readline(), which can block
forever before the timeout is checked again. Update the loop in the consistency
sync helper/test to use a non-blocking readiness check (for example via select
or equivalent) before reading from stdout, so the timeout can still be enforced
even when the subprocess produces no output. Apply the same fix to the
duplicated sync-waiting block referenced by the other occurrence in this test
file, and keep the existing guidance around verbose_sync, resampling_ran, and
the generated guidelines match handling intact.
altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py-48-51 (1)

48-51: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

parse_code_response produces garbage when no ``` fence is present.

When response contains no triple-backtick, find("```") returns -1. Line 49 then evaluates response[-1:] (last character), and line 51 evaluates response[:2] (first two chars of that). The function silently returns a 1–2 character string instead of an empty string or the original input. Add an early guard.

🛡️ Proposed fix
 def parse_code_response(response: str) -> str:
+    if "```" not in response:
+        return response.strip()
     # remove everything preceding the python code
     response = response[response.find("```"):]
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py`
around lines 48 - 51, `parse_code_response` in `sample_preprocessing.py`
mishandles responses without a triple-backtick fence because it slices using the
result of `find("```")` and `rfind("```")` even when no fence exists. Add an
early guard at the start of `parse_code_response` to detect the no-fence case
and return a safe result (such as the stripped original response) before the
existing fence-trimming logic runs. Keep the fix localized around the response
preprocessing block that currently removes text before and after the code fence.
docs/consistency_guidelines_integration.md-58-60 (1)

58-60: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

max_samples value doesn't match actual config.

The YAML example here shows max_samples: 10, but the actual agent_config.yaml has max_samples: 5. Update the docs to match the shipped default.

🤖 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 `@docs/consistency_guidelines_integration.md` around lines 58 - 60, The YAML
example for the sampling settings is out of sync with the shipped default, since
it shows a different max_samples value than the actual agent_config.yaml. Update
the example in the consistency guidelines so the max_samples entry matches the
real default used by the configuration, and keep the surrounding
aggregation/max_steps context unchanged.
altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml-7-8 (1)

7-8: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

max_samples value doesn't match documentation.

The actual config sets max_samples: 5, but docs/consistency_guidelines_integration.md (lines 58-60) shows max_samples: 10. Align one to the other to avoid confusion when users override via config_path=.

🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml` around
lines 7 - 8, The `agent_config.yaml` values are out of sync with the documented
consistency analyzer settings, specifically `max_samples` in the
`consistency_analyzer` config. Update either the YAML entry or the corresponding
documentation so the `max_samples` value matches across the `agent_config` and
the integration guide, and keep `max_steps` unchanged unless it is also intended
to align.
run_openai_agents_demo_with_tips.sh-71-76 (1)

71-76: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Comment mentions --consistency flag that doesn't exist in the command.

The comment says "Pass --consistency to use the resampling-based consistency guideline generator" but the evolve sync phoenix command below doesn't pass any such flag. Consistency mode is controlled by EVOLVE_GUIDELINES_MODE env var, not a CLI flag. Either add export EVOLVE_GUIDELINES_MODE=consistency before the command or remove the misleading comment.

🔧 Proposed fix
-# Pass --consistency to use the resampling-based consistency guideline generator
-# instead of the default generate_guidelines flow (requires the `consistency` extra).
+export EVOLVE_GUIDELINES_MODE=consistency
 uv run evolve sync phoenix \
🤖 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 `@run_openai_agents_demo_with_tips.sh` around lines 71 - 76, The comment above
the evolve sync command is misleading because it mentions a --consistency flag
that is not used by the `uv run evolve sync phoenix` invocation. Update the
script to either set `EVOLVE_GUIDELINES_MODE=consistency` before calling `evolve
sync phoenix` or remove the outdated comment so it matches the actual
`generate_guidelines`/consistency behavior controlled by the environment
variable.
altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py-174-182 (1)

174-182: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Metric label uses metric_config instead of this_config when alternates are present.

When alternates exist, this_config is set from find_matching_alternate and used for the actual consistency computation (line 178), but line 182 reads the metric label from the original metric_config. If the matched alternate has different fields or metric, the label will be wrong.

🔧 Proposed fix
-            metric = "mixed" if "fields" in metric_config else metric_config.get("metric", "mixed")
+            metric = "mixed" if "fields" in this_config else this_config.get("metric", "mixed")
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`
around lines 174 - 182, The metric label in single_step_consistency.py is being
derived from metric_config even when alternates are resolved into this_config,
so the label can mismatch the actual consistency config. Update the metric
assignment in the consistency calculation block to read from the resolved
this_config (the value returned by find_matching_alternate) rather than the
original metric_config, and make sure the fallback logic still works when
alternates are absent.
altk_evolve/llm/guidelines/consistency_analyzer/utils.py-55-55 (1)

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

Fix find_matching_alternate type annotation.

alternates is annotated as dict but is iterated as a list (for alternate in alternates) and called with metric_config["alternates"], which is a list from the YAML config. The annotation should be list.

🔧 Proposed fix
-def find_matching_alternate(alternates: dict, parsed_actual: dict) -> dict:
+def find_matching_alternate(alternates: list, parsed_actual: dict) -> dict:
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/utils.py` at line 55, The
`find_matching_alternate` signature uses the wrong type for `alternates`; it is
iterated like a list and passed `metric_config["alternates"]`, so update the
annotation in `find_matching_alternate` to reflect a list of alternates rather
than a dict. Make sure the parameter type matches the YAML-driven structure and
keep the return type unchanged if it still returns a single matching alternate.
README_DEMO_SCRIPTS.md-79-82 (1)

79-82: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fenced code blocks missing language identifiers.

Static analysis (markdownlint MD040) flags these fenced blocks as missing a language tag.

📝 Proposed fix
-```
+```text
 trajectory_openai_agents_105121.json
 generated_guidelines_openai_agents_105121.json
```diff
-```
+```text
 AuthenticationError: team not allowed to access model. This team can only access models=['Azure/gpt-4o', ...]
</details>


Also applies to: 198-200

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @README_DEMO_SCRIPTS.md around lines 79 - 82, Add a language identifier to
each fenced code block flagged by markdownlint MD040 in README_DEMO_SCRIPTS.md,
including the examples near trajectory_openai_agents_105121.json and the
AuthenticationError snippet. Update the affected markdown fences to use an
appropriate tag such as text so the blocks remain rendered correctly while
satisfying the lint rule.


</details>

<!-- cr-comment:v1:ce71200dd30fa0b6f8ec42d4 -->

_Source: Linters/SAST tools_

</blockquote></details>
<details>
<summary>altk_evolve/llm/guidelines/consistency_guidelines.py-219-245 (1)</summary><blockquote>

`219-245`: _🎯 Functional Correctness_ | _🟡 Minor_ | _⚡ Quick win_

**"Agent reasoning" branch assumes string content; list-type content (Agents SDK) isn't handled.**

This function's docstring context and `_can_segment_trajectory` both explicitly anticipate assistant `content` being a list of blocks (e.g., `function_call` items) for Agents SDK trajectories. Here, a non-`tool_calls` step with list content falls into the `else` branch and is treated as if `content` were a string (`len(content)`, slicing, `+ "..."`), producing the Python list's repr in the generated prompt instead of meaningful text.



<details>
<summary>🐛 Proposed fix</summary>

```diff
         else:
             step_type = "Agent reasoning"
-            content = step.get("content", "") or ""
+            content = step.get("content", "") or ""
+            if not isinstance(content, str):
+                content = str(content)
             if len(content) > 500:
                 content = content[:500] + "..."
             this_step_text = content
🤖 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 `@altk_evolve/llm/guidelines/consistency_guidelines.py` around lines 219 - 245,
The “Agent reasoning” path in the message formatting logic assumes
`step["content"]` is always a string, but Agents SDK assistant messages can
provide content as a list of blocks. Update the formatting in the assistant-step
loop to detect list content before the `len(content)`/slice handling, and
convert those blocks into readable text instead of using the list repr. Keep the
existing `tool_calls` handling in place, and make the change in the same
message-processing block that sets `step_type` and `this_step_text`.
altk_evolve/llm/guidelines/consistency_guidelines.py-398-407 (1)

398-407: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Exclude Groq from constrained decoding here. altk_evolve/llm/guidelines/consistency_guidelines.py:389-398 should match the other guideline paths and skip the JSON-schema branch for Groq-backed models; otherwise this flow can take the constrained-decoding path that the rest of the guideline pipeline avoids.

🤖 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 `@altk_evolve/llm/guidelines/consistency_guidelines.py` around lines 398 - 407,
The constrained decoding check in consistency_guidelines should explicitly skip
Groq-backed models so this path matches the other guideline flows. Update the
logic around get_supported_openai_params, supports_response_schema, and
constrained_decoding_supported to gate out llm_settings.custom_llm_provider ==
"groq" before enabling the JSON-schema branch, keeping the behavior aligned with
the rest of the guideline pipeline.
altk_evolve/llm/guidelines/consistency_guidelines.py-302-325 (1)

302-325: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Avoid the global litellm.enable_json_schema_validation toggle here. Pass enable_json_schema_validation= on completion() instead; mutating the module-level flag can leak across overlapping guideline calls in the same process.

🤖 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 `@altk_evolve/llm/guidelines/consistency_guidelines.py` around lines 302 - 325,
The guideline generation flow in the completion call path is mutating the
module-level litellm.enable_json_schema_validation flag, which can leak across
concurrent calls. Update the logic around the completion() invocations in the
constrained_decoding_supported branch to pass enable_json_schema_validation
directly on completion() instead of setting
litellm.enable_json_schema_validation globally, and remove the global toggle
from both branches while preserving the existing clean_llm_response handling in
consistency_guidelines.py.
🧹 Nitpick comments (9)
tests/e2e/test_e2e_consistency_pipeline.py (1)

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

Extract shared helpers to reduce duplication.

Both test functions share ~100 lines of near-identical code for agent execution (Step 1), trace verification (Step 2), and sync output monitoring (Step 3). Extracting these into helpers would prevent divergence and simplify future updates.

Suggested helpers:

  • _run_agent(script_path, project_name, timeout=90)subprocess.CompletedProcess
  • _verify_traces(phoenix_server, project_name)str (trace count)
  • _run_sync_monitor(process, timeout, verbose)tuple[bool, int, str] (resampling_ran, guideline_count, full_output)

The _consistency_analyzer_available() function (line 45) also duplicates _consistency_available() in test_e2e_mcp_consistency.py and test_e2e_smolagent_mcp.py — consider consolidating into a shared fixture in conftest.py.

🤖 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 `@tests/e2e/test_e2e_consistency_pipeline.py` around lines 57 - 399, Both E2E
tests duplicate the same agent-run, Phoenix trace-check, and sync-log polling
logic, so extract that repeated flow into shared helpers to keep the tests
aligned. Move the Step 1/2/3 behavior in `test_e2e_consistency_pipeline` and
`test_e2e_both_mode_smolagents` into helpers such as `_run_agent`,
`_verify_traces`, and `_run_sync_monitor`, then have both tests call those
helpers instead of inlining the subprocess and parsing code. Also consider
centralizing `_consistency_analyzer_available()` with the other availability
checks in a shared `conftest.py` fixture/helper so the consistency gating logic
is defined once.
altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py (1)

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

Extract a shared parse-dispatch helper to eliminate duplicated if/elif chains.

The same six-branch if/elif block mapping response_type to a parser function appears twice — once for sampled responses (lines 647–658) and once for the actual response (lines 681–692). Any new response type must be added in both places, risking divergence.

♻️ Proposed refactor
+def _parse_single_response(response, response_type: str):
+    """Dispatch to the appropriate parser based on response_type."""
+    if response_type == "code":
+        return parse_code_response(response)
+    elif response_type == "json":
+        return parse_json_response(response)
+    elif response_type == "react":
+        return parse_react_response(response)
+    elif response_type == "react_aw":
+        return parse_react_aw_response(response)
+    elif response_type == "thought_code":
+        return parse_thought_code_response(response)
+    elif response_type == "tool_calls":
+        return parse_tool_calls_response(response)
+    return response
+
 def extract_parsed_responses_from_trajectory(trajectory:dict, config: dict)-> dict:
     ...
     for response in response_samples:
-        if agent_config["response_type"] == "code":
-            parsed_response = parse_code_response(response)
-        elif agent_config["response_type"] == "json":
-            parsed_response = parse_json_response(response)
-        ...
+        parsed_response = _parse_single_response(response, agent_config["response_type"])
     ...
     actual_response = step.get("raw_response", "")
-    if agent_config["response_type"] == "code":
-        parsed_response = parse_code_response(actual_response)
-    elif agent_config["response_type"] == "json":
-        parsed_response = parse_json_response(actual_response)
-    ...
+    parsed_response = _parse_single_response(actual_response, agent_config["response_type"])
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py`
around lines 647 - 693, The response-type parsing logic is duplicated in the
sampled-response loop and the actual-response path, making future updates easy
to miss. Extract the `response_type` to parser mapping into a shared helper in
`sample_preprocessing.py` and have both the sampling block and the
`step["raw_response"]` handling call it, so `parse_code_response`,
`parse_json_response`, `parse_react_response`, `parse_react_aw_response`,
`parse_tool_calls_response`, and `parse_thought_code_response` are all
dispatched from one place.
altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py (1)

85-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Docstring references pmi aggregation mode but no implementation exists.

get_agg_fcn lists 'pmi' in its docstring (line 90) and the ConsistencyAggregator class docstring (line 114) mentions PMI support, but there is no pmi branch in the function. Either remove the reference or add a stub that raises a clear "not implemented" error.

🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py`
around lines 85 - 107, The aggregation selector in get_agg_fcn is inconsistent
with its own documentation because it advertises pmi support but has no matching
branch. Update get_agg_fcn and the ConsistencyAggregator docs so they agree:
either remove pmi from the described modes or add an explicit pmi branch that
raises a clear not-implemented error, and keep the existing mean, rms, geo_mean,
and product handling unchanged.
docs/consistency_guidelines_integration.md (1)

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

Add language specifiers to fenced code blocks.

Three fenced code blocks (lines 121, 129, 162) lack language tags, triggering markdownlint MD040 warnings. Use bash for the CLI/ENV examples.

Also applies to: 129-129, 162-162

🤖 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 `@docs/consistency_guidelines_integration.md` at line 121, The fenced code
blocks in the consistency guidelines markdown are missing language specifiers,
which triggers markdownlint MD040 warnings. Update each affected fenced block in
this section to include the appropriate language tag, using bash for the CLI and
environment example blocks, and ensure the same fix is applied consistently to
the other affected fences in the document.

Source: Linters/SAST tools

altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml (1)

23-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out debug lines.

These two lines (# response_type: tool_calls / # metric: jaccard) under AnyAgent_content are leftover scratch with inconsistent indentation. Either delete them or convert to a proper YAML comment explaining the alternative config.

🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml` around
lines 23 - 24, Remove the leftover commented-out debug lines under
AnyAgent_content in agent_config.yaml: the commented response_type and metric
entries are scratch artifacts with inconsistent indentation. Either delete those
comments entirely or rewrite them as a single properly indented YAML comment
that clearly explains the alternative configuration, keeping the surrounding
AnyAgent_content block tidy.
altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py (1)

53-53: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider >= instead of > for minimum sample threshold.

len(field_samples) > min_samples requires strictly more than MIN_FRACTION * max_samples samples. With max_samples=5, min_samples=2, so you need 3+ samples (60%) rather than 2+ (40%). If the intent is "at least half," use >=.

🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`
at line 53, The sample-threshold check in single_step_consistency.py is too
strict because it uses `len(field_samples) > min_samples`, which excludes cases
where the sample count exactly meets the minimum. Update the condition in the
consistency analysis logic to use `>=` instead of `>` so the `field_samples`
threshold in the relevant branch is inclusive and matches the intended
minimum-sample behavior.
altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py (1)

456-462: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use np.triu_indices instead of filtering by != 0.

Filtering flattened_array[flattened_array != 0] to extract the upper triangle is fragile — a legitimate cosine similarity of exactly 0.0 (orthogonal embeddings) would be silently excluded from the mean. Use np.triu_indices for correct index-based extraction.

♻️ Proposed refactor
         # extract the upper triangle and zero-out the lower triangle
-        upper_half = np.triu(np.asarray(similarities), k=1)
-        flattened_array = upper_half.flatten()
-        # remove the zeroes from the lower triangle
-        embedding_pairwise_similarities = flattened_array[flattened_array != 0]
+        sim_array = np.asarray(similarities)
+        n = sim_array.shape[0]
+        embedding_pairwise_similarities = sim_array[np.triu_indices(n, k=1)]
         # aggregate over all pairwise similarities
         mean_embedding_similarity = np.mean(embedding_pairwise_similarities)
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py` around
lines 456 - 462, The pairwise similarity aggregation in consistency_metric.py is
incorrectly removing legitimate 0.0 cosine similarities by filtering
`flattened_array != 0`, which can skew `mean_embedding_similarity`. Update the
logic that builds `embedding_pairwise_similarities` to use `np.triu_indices` on
the similarities array instead of flatten-and-filter, so the upper-triangle
values are selected by index rather than by value. Keep the rest of the mean
calculation the same, but make sure the extraction happens from the
`similarities` matrix directly.
altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py (1)

21-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mutable default argument tools: list = [].

Not currently mutated, but a common footgun if a future edit appends to tools in place — the empty list would be shared across all calls.

🧹 Proposed fix
-    tools: list = [],
+    tools: list | None = None,
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py` around
lines 21 - 30, The get_response_sampling function currently uses a mutable
default for tools, which can be shared across calls if it is ever modified in
place. Update the function signature to use a non-mutable default and initialize
a fresh list inside get_response_sampling when tools is not provided, keeping
the existing behavior for callers while preventing shared state.
altk_evolve/frontend/mcp/mcp_server.py (1)

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

Duplicated entity-construction logic between "regular" and "consistency" branches (and phoenix_sync.py).

Both branches build near-identical Entity(...) list comprehensions differing only in generation_method and the results source; the same pattern is repeated in phoenix_sync.py. Consider extracting a shared helper (e.g., _build_guideline_entities(results, metadata_base, generation_method)).

🤖 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 `@altk_evolve/frontend/mcp/mcp_server.py` around lines 534 - 577, The guideline
entity construction is duplicated between the regular and consistency branches,
making the MCP server harder to maintain and also mirrored in phoenix_sync.py.
Extract the shared `Entity(...)` list-building logic from the
`guideline_entities` assembly into a helper such as
`_build_guideline_entities(results, metadata_base, generation_method)` and use
it in both `generate_guidelines` and `generate_consistency_guidelines` paths,
keeping only the result source and generation method different.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@altk_evolve/frontend/mcp/mcp_server.py`:
- Around line 556-560: The MCP trajectory payload is incomplete because it only
forwards messages and trace_id, so tool-calling steps lose model and tools
context compared with the Phoenix-sync path. Update the trajectory built in the
MCP save path around generate_consistency_guidelines/transform_trajectory_to_IR
to include the same richer fields (especially model and tools, plus any other
trajectory metadata already available) so tool_calls can be named and resampled
with the real tool schemas.
- Around line 536-577: Wrap the guideline generation in mcp_server.py with error
handling so failures in generate_guidelines or generate_consistency_guidelines
do not abort the whole request after the trajectory has already been persisted.
Add try/except around the regular and consistency branches in the guideline
entity assembly path, log or record the exception with task context (for example
task_id and guidelines_mode), and fall back to returning the saved
trajectory/read-back response with an empty or partial guideline_entities list.
Reference the generate_guidelines, generate_consistency_guidelines, and
guideline_entities assembly blocks when making the fix.

In `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_analysis.py`:
- Around line 18-30: The aggregate trajectory uncertainty calculation in
consistency_analysis.py should handle the same -1 sentinel used for step-level
uncertainty instead of always subtracting from 1.0. Update the logic in the
return block that builds the trajectory summary so aggregate_step_consistency
values of -1 (and any missing/default sentinel) map to
aggregate_trajectory_uncertainty = -1, while valid consistency scores still use
1.0 - value. Keep the behavior consistent with the step processing in the same
function and avoid producing out-of-range values.

In `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py`:
- Around line 26-38: The caching helpers `get_sentence_transformer_small` and
`get_sentence_transformer_large` are only updating a local parameter, so the
module-level model variables never retain the loaded `SentenceTransformer`
instance. Update these functions to assign to the actual shared state in
`consistency_metric.py` (for example by declaring the corresponding global model
variables or otherwise removing the shadowing), so
`get_metric_instance("sbert_small")` and the large-model path reuse the same
cached object instead of reloading on every call.
- Around line 506-522: The NumericFractionConsistencyMetric._get_distance
implementation currently returns a raw mean absolute difference, which can
exceed 1 and break the 1.0 - distance consistency calculation. Update
_get_distance in consistency_metric.py to normalize the computed distance into
the [0, 1] range before returning it, while preserving the no-samples fallback
of -1.0. Ensure get_consistency_and_distance continues to rely on this
normalized value so consistency scores stay within the module’s expected bounds.

In `@altk_evolve/llm/guidelines/consistency_analyzer/resampling.py`:
- Around line 66-102: The old-format CUGA resampling path in resampling.py is
unreachable because the early skip for missing llm_params returns before that
fallback can run. Update the step-processing logic in the resampling loop so the
legacy prompts-based handling is checked before any continue that skips steps
without llm_params. Keep the existing llm_params path for newer trajectories,
but make the old CUGA branch (the prompt_item/human_prompt_item handling)
reachable when step["prompts"] exists and llm_params is absent.
- Around line 61-64: The resampling loop in `resampling.py` is exiting too early
and the old-format CUGA fallback is unreachable. In the logic around the step
checks (including the `if "sampling" in step` guard and the `if "llm_params" not
in step` branch), replace the early exit with a per-step skip so later steps
still get processed, and restructure the CUGA handling so the legacy `else` path
can actually run when `llm_params` is absent. Keep the behavior localized inside
the resampling pass rather than terminating the whole function from the first
matching step.

In `@altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py`:
- Around line 224-228: Replace the bare exception handlers in
sample_preprocessing.py with narrower catches. In the JSON parsing block around
the response cleanup and json.loads call, change the generic except clauses to
except Exception, or even better except json.JSONDecodeError for the parse step.
Also update the BaseException handler later in the same module to Exception so
SystemExit and KeyboardInterrupt are not swallowed.

In `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`:
- Around line 126-129: The `tool_calls` path in `check_sample_validity` is
validating `raw_samples`, but `compute_step_consistency` and
`compute_json_step_consistency` actually use `parsed_samples`, so the checks are
out of sync. Update `check_sample_validity` to treat `tool_calls` like the
structured/JSON cases used by `compute_step_consistency`, and validate
`parsed_samples` instead of `raw_samples` so empty parsed tool-call samples are
rejected before consistency is computed.

In `@altk_evolve/llm/guidelines/consistency_analyzer/utils.py`:
- Around line 187-190: In compute_weighted_sum_consistency, avoid mutating
field["name"] when handling the list case; instead, compute a local normalized
name variable from field["name"] and use that for the field_consistencies lookup
and weight update. Keep the original field dict unchanged so callers reusing
step_cns_list do not see side effects.

In `@altk_evolve/sync/phoenix_sync.py`:
- Around line 812-885: The `_process_trajectory()` flow marks a trace as
processed before the guideline pipelines complete, so failures in
`generate_guidelines()` or `generate_consistency_guidelines()` can prevent
retries. Update the logic around the trajectory write and
`self.client.update_entities()` so the trace is only considered processed after
guideline generation succeeds, or record a retryable failure state instead; use
the existing `_process_trajectory`, `guideline_entities`, and
`_get_processed_trace_ids` flow to keep failed traces eligible for a later
retry.

In `@scripts/extract_trajectories.py`:
- Around line 150-154: The output handling in extract_trajectories.py is
incorrectly filtering assistant messages on content, which drops completion
spans that only have tool_calls. Update the output message loop in the extractor
to accept assistant output entries when tool call data is present under
llm.output_messages.{i}.message.tool_calls, and build the completion record from
the role plus tool_calls even when content is absent. Use the existing output
message parsing around the loop over output_indices and the messages.append path
to make the change without relying on content as the only condition.

In `@tests/e2e/test_e2e_consistency_pipeline.py`:
- Around line 45-52: The availability guard in _consistency_analyzer_available()
is importing the wrong module path, so it always fails and skips the E2E flow.
Update the importlib.import_module check to use the vendored
consistency_analyzer path under altk_evolve.llm.guidelines.consistency_analyzer,
matching the module path used by the other consistency tests.

In `@tests/e2e/test_e2e_smolagent_mcp.py`:
- Around line 136-180: The e2e smolagent MCP test can pass on stale
`guidelines_*.json` files left in `consistency_debug`, so clean that directory
(or at least remove existing guidelines artifacts) before running
`_run_smolagent_and_extract_messages()` and `save_trajectory`. Use the existing
`debug_dir` setup in `test_e2e_smolagent_mcp.py` and ensure the verification
after `Client.call_tool_mcp("save_trajectory", ...)` only sees files produced by
the current run.

---

Minor comments:
In `@altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml`:
- Around line 7-8: The `agent_config.yaml` values are out of sync with the
documented consistency analyzer settings, specifically `max_samples` in the
`consistency_analyzer` config. Update either the YAML entry or the corresponding
documentation so the `max_samples` value matches across the `agent_config` and
the integration guide, and keep `max_steps` unchanged unless it is also intended
to align.

In `@altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py`:
- Around line 48-51: `parse_code_response` in `sample_preprocessing.py`
mishandles responses without a triple-backtick fence because it slices using the
result of `find("```")` and `rfind("```")` even when no fence exists. Add an
early guard at the start of `parse_code_response` to detect the no-fence case
and return a safe result (such as the stripped original response) before the
existing fence-trimming logic runs. Keep the fix localized around the response
preprocessing block that currently removes text before and after the code fence.

In `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`:
- Around line 174-182: The metric label in single_step_consistency.py is being
derived from metric_config even when alternates are resolved into this_config,
so the label can mismatch the actual consistency config. Update the metric
assignment in the consistency calculation block to read from the resolved
this_config (the value returned by find_matching_alternate) rather than the
original metric_config, and make sure the fallback logic still works when
alternates are absent.

In `@altk_evolve/llm/guidelines/consistency_analyzer/utils.py`:
- Line 55: The `find_matching_alternate` signature uses the wrong type for
`alternates`; it is iterated like a list and passed
`metric_config["alternates"]`, so update the annotation in
`find_matching_alternate` to reflect a list of alternates rather than a dict.
Make sure the parameter type matches the YAML-driven structure and keep the
return type unchanged if it still returns a single matching alternate.

In `@altk_evolve/llm/guidelines/consistency_guidelines.py`:
- Around line 219-245: The “Agent reasoning” path in the message formatting
logic assumes `step["content"]` is always a string, but Agents SDK assistant
messages can provide content as a list of blocks. Update the formatting in the
assistant-step loop to detect list content before the `len(content)`/slice
handling, and convert those blocks into readable text instead of using the list
repr. Keep the existing `tool_calls` handling in place, and make the change in
the same message-processing block that sets `step_type` and `this_step_text`.
- Around line 398-407: The constrained decoding check in consistency_guidelines
should explicitly skip Groq-backed models so this path matches the other
guideline flows. Update the logic around get_supported_openai_params,
supports_response_schema, and constrained_decoding_supported to gate out
llm_settings.custom_llm_provider == "groq" before enabling the JSON-schema
branch, keeping the behavior aligned with the rest of the guideline pipeline.
- Around line 302-325: The guideline generation flow in the completion call path
is mutating the module-level litellm.enable_json_schema_validation flag, which
can leak across concurrent calls. Update the logic around the completion()
invocations in the constrained_decoding_supported branch to pass
enable_json_schema_validation directly on completion() instead of setting
litellm.enable_json_schema_validation globally, and remove the global toggle
from both branches while preserving the existing clean_llm_response handling in
consistency_guidelines.py.

In `@docs/consistency_guidelines_integration.md`:
- Around line 58-60: The YAML example for the sampling settings is out of sync
with the shipped default, since it shows a different max_samples value than the
actual agent_config.yaml. Update the example in the consistency guidelines so
the max_samples entry matches the real default used by the configuration, and
keep the surrounding aggregation/max_steps context unchanged.

In `@README_DEMO_SCRIPTS.md`:
- Around line 79-82: Add a language identifier to each fenced code block flagged
by markdownlint MD040 in README_DEMO_SCRIPTS.md, including the examples near
trajectory_openai_agents_105121.json and the AuthenticationError snippet. Update
the affected markdown fences to use an appropriate tag such as text so the
blocks remain rendered correctly while satisfying the lint rule.

In `@run_openai_agents_demo_with_tips.sh`:
- Around line 71-76: The comment above the evolve sync command is misleading
because it mentions a --consistency flag that is not used by the `uv run evolve
sync phoenix` invocation. Update the script to either set
`EVOLVE_GUIDELINES_MODE=consistency` before calling `evolve sync phoenix` or
remove the outdated comment so it matches the actual
`generate_guidelines`/consistency behavior controlled by the environment
variable.

In `@tests/e2e/test_e2e_consistency_pipeline.py`:
- Around line 172-204: The consistency-sync polling loop uses
process.stdout.readline(), which can block forever before the timeout is checked
again. Update the loop in the consistency sync helper/test to use a non-blocking
readiness check (for example via select or equivalent) before reading from
stdout, so the timeout can still be enforced even when the subprocess produces
no output. Apply the same fix to the duplicated sync-waiting block referenced by
the other occurrence in this test file, and keep the existing guidance around
verbose_sync, resampling_ran, and the generated guidelines match handling
intact.

---

Nitpick comments:
In `@altk_evolve/frontend/mcp/mcp_server.py`:
- Around line 534-577: The guideline entity construction is duplicated between
the regular and consistency branches, making the MCP server harder to maintain
and also mirrored in phoenix_sync.py. Extract the shared `Entity(...)`
list-building logic from the `guideline_entities` assembly into a helper such as
`_build_guideline_entities(results, metadata_base, generation_method)` and use
it in both `generate_guidelines` and `generate_consistency_guidelines` paths,
keeping only the result source and generation method different.

In `@altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml`:
- Around line 23-24: Remove the leftover commented-out debug lines under
AnyAgent_content in agent_config.yaml: the commented response_type and metric
entries are scratch artifacts with inconsistent indentation. Either delete those
comments entirely or rewrite them as a single properly indented YAML comment
that clearly explains the alternative configuration, keeping the surrounding
AnyAgent_content block tidy.

In `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py`:
- Around line 85-107: The aggregation selector in get_agg_fcn is inconsistent
with its own documentation because it advertises pmi support but has no matching
branch. Update get_agg_fcn and the ConsistencyAggregator docs so they agree:
either remove pmi from the described modes or add an explicit pmi branch that
raises a clear not-implemented error, and keep the existing mean, rms, geo_mean,
and product handling unchanged.

In `@altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py`:
- Around line 456-462: The pairwise similarity aggregation in
consistency_metric.py is incorrectly removing legitimate 0.0 cosine similarities
by filtering `flattened_array != 0`, which can skew `mean_embedding_similarity`.
Update the logic that builds `embedding_pairwise_similarities` to use
`np.triu_indices` on the similarities array instead of flatten-and-filter, so
the upper-triangle values are selected by index rather than by value. Keep the
rest of the mean calculation the same, but make sure the extraction happens from
the `similarities` matrix directly.

In `@altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py`:
- Around line 21-30: The get_response_sampling function currently uses a mutable
default for tools, which can be shared across calls if it is ever modified in
place. Update the function signature to use a non-mutable default and initialize
a fresh list inside get_response_sampling when tools is not provided, keeping
the existing behavior for callers while preventing shared state.

In `@altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py`:
- Around line 647-693: The response-type parsing logic is duplicated in the
sampled-response loop and the actual-response path, making future updates easy
to miss. Extract the `response_type` to parser mapping into a shared helper in
`sample_preprocessing.py` and have both the sampling block and the
`step["raw_response"]` handling call it, so `parse_code_response`,
`parse_json_response`, `parse_react_response`, `parse_react_aw_response`,
`parse_tool_calls_response`, and `parse_thought_code_response` are all
dispatched from one place.

In `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`:
- Line 53: The sample-threshold check in single_step_consistency.py is too
strict because it uses `len(field_samples) > min_samples`, which excludes cases
where the sample count exactly meets the minimum. Update the condition in the
consistency analysis logic to use `>=` instead of `>` so the `field_samples`
threshold in the relevant branch is inclusive and matches the intended
minimum-sample behavior.

In `@docs/consistency_guidelines_integration.md`:
- Line 121: The fenced code blocks in the consistency guidelines markdown are
missing language specifiers, which triggers markdownlint MD040 warnings. Update
each affected fenced block in this section to include the appropriate language
tag, using bash for the CLI and environment example blocks, and ensure the same
fix is applied consistently to the other affected fences in the document.

In `@tests/e2e/test_e2e_consistency_pipeline.py`:
- Around line 57-399: Both E2E tests duplicate the same agent-run, Phoenix
trace-check, and sync-log polling logic, so extract that repeated flow into
shared helpers to keep the tests aligned. Move the Step 1/2/3 behavior in
`test_e2e_consistency_pipeline` and `test_e2e_both_mode_smolagents` into helpers
such as `_run_agent`, `_verify_traces`, and `_run_sync_monitor`, then have both
tests call those helpers instead of inlining the subprocess and parsing code.
Also consider centralizing `_consistency_analyzer_available()` with the other
availability checks in a shared `conftest.py` fixture/helper so the consistency
gating logic is defined once.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c9cfa75-dcfd-4cdc-a90d-65dc16965298

📥 Commits

Reviewing files that changed from the base of the PR and between d44a402 and dac68b9.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • .env.example
  • .gitignore
  • README_DEMO_SCRIPTS.md
  • altk_evolve/frontend/mcp/mcp_server.py
  • altk_evolve/llm/guidelines/consistency_analyzer/__init__.py
  • altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_analysis.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py
  • altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py
  • altk_evolve/llm/guidelines/consistency_analyzer/resampling.py
  • altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py
  • altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py
  • altk_evolve/llm/guidelines/consistency_analyzer/utils.py
  • altk_evolve/llm/guidelines/consistency_guidelines.py
  • altk_evolve/llm/guidelines/prompts/generate_consistency_guidelines.jinja2
  • altk_evolve/sync/phoenix_sync.py
  • docs/consistency_guidelines_integration.md
  • pyproject.toml
  • run_openai_agents_demo_with_tips.sh
  • scripts/extract_trajectories.py
  • tests/e2e/conftest.py
  • tests/e2e/test_e2e_consistency_pipeline.py
  • tests/e2e/test_e2e_mcp_consistency.py
  • tests/e2e/test_e2e_pipeline.py
  • tests/e2e/test_e2e_smolagent_mcp.py
  • tests/unit/test_cli.py
  • tests/unit/test_consistency_analyzer.py
  • tests/unit/test_consistency_guidelines.py
  • tests/unit/test_mcp_server.py
  • tests/unit/test_phoenix_sync.py
💤 Files with no reviewable changes (1)
  • tests/e2e/test_e2e_pipeline.py

Comment thread altk_evolve/frontend/mcp/mcp_server.py Outdated
Comment thread altk_evolve/frontend/mcp/mcp_server.py Outdated
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/consistency_analysis.py Outdated
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py Outdated
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py Outdated
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/utils.py Outdated
Comment thread altk_evolve/sync/phoenix_sync.py
Comment thread scripts/extract_trajectories.py Outdated
Comment thread tests/e2e/test_e2e_consistency_pipeline.py
Comment thread tests/e2e/test_e2e_smolagent_mcp.py
evduester and others added 14 commits July 8, 2026 12:43
Trajectory is already durably persisted before guidelines run; a transient
LLM/network error during generation should log a warning and return the
trajectory readback rather than aborting the whole call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…step naming

Without the OpenAI tools schema, transform_trajectory_to_IR always uses the
AnyAgent prefix and skips attaching tool definitions to tool_calls steps,
degrading resampling quality. tools is optional and JSON-encoded to stay
MCP-protocol compatible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…entinel

When the aggregator returns -1 (no scoreable steps), 1.0 - (-1) produced 2.0
which is outside [0,1] and inconsistent with step-level handling. Mirror the
same sentinel check used for step_uncertainty on line 18.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parameter names shadowed the module-level globals, so assignments inside the
functions only updated local variables and the global stayed None forever —
causing a full model reload on every call. Drop the parameters and use global
declarations instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… to [0,1)

Raw mean absolute difference is unbounded, so 1.0 - distance could go
negative for any difference > 1. Apply d / (1 + d) normalization which
maps [0, ∞) → [0, 1) monotonically, keeping consistency scores in [0, 1].
Applied consistently in both _get_distance and get_distance_from_chosen_trajectory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… branch

Two bugs in resample_trajectory:
- 'return trajectory' on an already-sampled step aborted the whole loop,
  leaving later steps unsampled; changed to 'continue'
- The old-format CUGA else branch was unreachable dead code; removed and
  simplified the step dispatch to a flat guard + direct llm_params path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bare except: and except BaseException: catch SystemExit/KeyboardInterrupt,
masking critical signals. All three sites are JSON parsing contexts;
replaced with except (json.JSONDecodeError, ValueError).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
check_sample_validity validated raw_samples for tool_calls, but
compute_step_consistency reads parsed_samples for the same response type.
Move tool_calls into the parsed_samples branch of check_sample_validity
so validation catches missing parsed data before computation runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m_consistency

List-to-string join was done in-place on the caller's dict, causing a
surprising side effect if step_cns_list is reused. Use a local variable instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cceeds

Previously the trajectory entity was written before guideline generation ran.
If generation failed, the trace was already marked processed and would never
be retried. Now the trajectory write is buffered and only committed after
generation completes successfully, keeping failed traces eligible for retry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tories

The output message loop only gated on content is not None, silently dropping
assistant completion spans that carry tool_calls but no content. Mirror the
same pattern used by the input loop: accept the message when tool_call keys
are present and extract them into the OpenAI tool_calls format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
consistency_analyzer is vendored under altk_evolve.llm.guidelines.consistency_analyzer;
the top-level path never resolves so the guard always returned False and all
consistency e2e tests were unconditionally skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… test

The debug directory is shared across e2e tests. Without clearing guidelines_*.json
before the run, the completion assertion at line 177 could pass on artifacts
from a previous test, giving a false positive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
msg: dict on line 150 shadowed the msg variable from line 105 in the same
function scope, triggering mypy's no-redef check. Drop the annotation since
mypy can infer the type from the dict literal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py (1)

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

Potential off-by-one: > should likely be >= for min_samples threshold.

min_samples is computed as int(MIN_FRACTION * max_samples) (line 184), where MIN_FRACTION = 0.5. The strict > means you need more than 50% of samples, not at least 50%. For example, with max_samples=2, min_samples=1, but len(field_samples) > 1 requires 2+ samples (100%). Fields with exactly min_samples parsed samples are silently skipped, potentially yielding undefined consistency (-1) when computation is feasible.

If the intent is "at least MIN_FRACTION of samples must be parsed," use >=:

-        if field_samples and len(field_samples) > min_samples and metric != "None":
+        if field_samples and len(field_samples) >= min_samples and metric != "None":
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`
at line 54, The sample-threshold check in `single_step_consistency` is too
strict because it uses `len(field_samples) > min_samples`, which skips fields
that meet the intended minimum exactly. Update the condition in
`single_step_consistency` to allow fields with at least the computed threshold
by changing the comparison to include equality, and keep the existing
`field_samples` and `metric != "None"` guards intact.
🧹 Nitpick comments (2)
tests/unit/test_consistency_analyzer.py (1)

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

Add @pytest.mark.unit markers to test classes.

As per coding guidelines, unit tests should use the pytest.mark.unit marker. None of the test classes in this file carry the marker. Add it at the class level or configure it via pyproject.toml/conftest.py if already handled globally.

♻️ Example: adding the marker to a test class
+import pytest
+
+
+@pytest.mark.unit
 class TestInvertListOfDictionaries:
     def test_basic_inversion(self):
🤖 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 `@tests/unit/test_consistency_analyzer.py` around lines 15 - 180, The unit test
classes in TestInvertListOfDictionaries, TestFlattenResponse,
TestExtractFieldValuesFromResponses, TestFindMatchingAlternate,
TestRescaleWeights, and TestComputeWeightedSumConsistency are missing the
required pytest.mark.unit marker. Add the marker at the class level for each
test class in test_consistency_analyzer.py, or confirm the marker is applied
globally via existing pytest configuration if that is already the project’s
approach.

Source: Coding guidelines

altk_evolve/llm/guidelines/consistency_analyzer/resampling.py (1)

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

Log denominator uses full step count instead of potentially truncated steps.

When max_steps != -1, steps is truncated but the progress log shows len(trajectory['steps']) (full count) as the denominator.

🔧 Suggested fix
-        logger.info(f"+++ Resampling step: {step['name']} ({j + 1}/{len(trajectory['steps'])})")
+        logger.info(f"+++ Resampling step: {step['name']} ({j + 1}/{len(steps)})")
🤖 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 `@altk_evolve/llm/guidelines/consistency_analyzer/resampling.py` at line 65,
The resampling progress log in the loop over steps uses the full trajectory
length as the denominator even when `steps` has been truncated by `max_steps`.
Update the `logger.info` call in `resample` to use the length of the actual
`steps` sequence being iterated so the progress count matches the processed
subset. Keep the message format the same, but reference the truncated collection
instead of `trajectory['steps']`.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`:
- Around line 120-126: The validation path is directly indexing
step["parsed_response"] when alternates are enabled, which can raise KeyError
instead of returning a false validity result. Update check_sample_validity to
verify the field exists before calling find_matching_alternate/flatten_response,
and return the existing invalid tuple with a clear message when it is missing;
apply the same guard in compute_step_consistency where the parsed response is
used downstream. Use the check_sample_validity and compute_step_consistency
logic to locate and wrap all parsed_response access with an existence check.

---

Outside diff comments:
In `@altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py`:
- Line 54: The sample-threshold check in `single_step_consistency` is too strict
because it uses `len(field_samples) > min_samples`, which skips fields that meet
the intended minimum exactly. Update the condition in `single_step_consistency`
to allow fields with at least the computed threshold by changing the comparison
to include equality, and keep the existing `field_samples` and `metric !=
"None"` guards intact.

---

Nitpick comments:
In `@altk_evolve/llm/guidelines/consistency_analyzer/resampling.py`:
- Line 65: The resampling progress log in the loop over steps uses the full
trajectory length as the denominator even when `steps` has been truncated by
`max_steps`. Update the `logger.info` call in `resample` to use the length of
the actual `steps` sequence being iterated so the progress count matches the
processed subset. Keep the message format the same, but reference the truncated
collection instead of `trajectory['steps']`.

In `@tests/unit/test_consistency_analyzer.py`:
- Around line 15-180: The unit test classes in TestInvertListOfDictionaries,
TestFlattenResponse, TestExtractFieldValuesFromResponses,
TestFindMatchingAlternate, TestRescaleWeights, and
TestComputeWeightedSumConsistency are missing the required pytest.mark.unit
marker. Add the marker at the class level for each test class in
test_consistency_analyzer.py, or confirm the marker is applied globally via
existing pytest configuration if that is already the project’s approach.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75356c54-c06a-44df-a5f3-7c817742f145

📥 Commits

Reviewing files that changed from the base of the PR and between dac68b9 and db615fc.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • altk_evolve/frontend/mcp/mcp_server.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_analysis.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py
  • altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py
  • altk_evolve/llm/guidelines/consistency_analyzer/resampling.py
  • altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py
  • altk_evolve/llm/guidelines/consistency_analyzer/single_step_consistency.py
  • altk_evolve/llm/guidelines/consistency_analyzer/utils.py
  • altk_evolve/llm/guidelines/consistency_guidelines.py
  • altk_evolve/sync/phoenix_sync.py
  • pyproject.toml
  • scripts/extract_trajectories.py
  • tests/e2e/test_e2e_consistency_pipeline.py
  • tests/e2e/test_e2e_mcp_consistency.py
  • tests/e2e/test_e2e_smolagent_mcp.py
  • tests/unit/test_consistency_analyzer.py
  • tests/unit/test_consistency_guidelines.py
  • tests/unit/test_mcp_server.py
  • tests/unit/test_phoenix_sync.py
💤 Files with no reviewable changes (1)
  • altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py
🚧 Files skipped from review as they are similar to previous changes (14)
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_analysis.py
  • altk_evolve/frontend/mcp/mcp_server.py
  • tests/unit/test_phoenix_sync.py
  • tests/unit/test_consistency_guidelines.py
  • tests/e2e/test_e2e_consistency_pipeline.py
  • tests/e2e/test_e2e_mcp_consistency.py
  • altk_evolve/sync/phoenix_sync.py
  • tests/unit/test_mcp_server.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_metric.py
  • tests/e2e/test_e2e_smolagent_mcp.py
  • altk_evolve/llm/guidelines/consistency_analyzer/utils.py
  • altk_evolve/llm/guidelines/consistency_analyzer/consistency_aggregator.py
  • scripts/extract_trajectories.py
  • altk_evolve/llm/guidelines/consistency_analyzer/sample_preprocessing.py

evduester and others added 8 commits July 15, 2026 15:41
'pmi' was listed in the get_agg_fcn docstring and ConsistencyAggregator
class description but has no implementation; passing it falls through to
the else-raise branch. Remove it from both docstrings so callers aren't
misled into using it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…shape

The documented score_card keys (trajectory_name, task_score,
aggregate_consistency, step_consistencies) shared zero overlap with the
actual keys returned by create_consistency_score_card (task, total_steps,
aggregation, aggregate_trajectory_uncertainty, steps). Updated to match
the real return value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With MAX_SAMPLES=2 and MIN_FRACTION=0.5, min_samples=1. The strict >
required len(field_samples) > 1 (100% of samples), not the intended >=50%.
Fields with exactly min_samples parsed samples were silently skipped,
returning -1 consistency when computation was feasible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… loops

process.stdout.readline() blocks until a line is available, so the timeout
check at the top of the loop was never reached if the subprocess stalled
without producing output. Adding a select() poll (0.5s) makes the loop
re-evaluate the timeout on every iteration and avoids hanging the test
indefinitely if the sync process hangs silently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve conflicts in mcp_server.py and phoenix_sync.py:
- Retain consistency-pipeline split (regular/consistency/both) with
  per-pipeline try/except guards from this branch
- Add support=1 field introduced by the consolidation PR (#283) to
  guideline entity metadata in both files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@evduester
evduester requested a review from jayaramkr July 15, 2026 20:33

@jayaramkr jayaramkr left a comment

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.

Re-reviewed against the updated branch (now merged with today's main). This is a big step up — every blocker from the last round is genuinely fixed, and I re-ran each of my original repros to confirm, not just read the diff. Requesting changes only for one remaining correctness gap plus a few medium items; the details are in the inline comments and the bulk of the work here is solid.

My original findings — status (all verified by re-running my repros)

Prior finding Status
B1 agent_config.yaml not packaged Fixed — now ships in the wheel (package-data + MANIFEST.in)
B2 .env ignored (raw os.environ) Fixed — new GuidelinesSettings(BaseSettings); verified .env is honored
B3 negative cosine → aggregator ValueError Fixedmax(0.0, mean); confirmed NaN can't reach the >= 0 guard
B4 jaccard("","") == 1.0 signal inversion Fixed — returns 0.0; no collateral (empties are filtered upstream)
B5 format_trajectory_data crashes (4 shapes) ⚠️ Partially — no longer crashes, but one case drops the tool name (see inline)
H both-mode discards regular guidelines on consistency failure Fixed — try/except; reproduced that regular guidelines + trajectory survive
H MCP save_trajectory bare except swallowing errors Fixed — per-pipeline try/except + logger.error(exc_info=True)
H --guidelines-mode CLI flag missing Fixed — restored, works end-to-end
hardcoded thresholds / is_groq guard / clean_llm_response on both branches / no step cap / unguarded debug writes All fixed

The cosine clamp, the jaccard fix, and the NumericFraction pairwise-loop fix are all correct and introduce no new bug (verified by execution).

Still open / new (ranked)

  • H1 (correctness, still open)agent_config.yaml still defines only 3 of the 6 step names the IR produces. AnyAgent_tool_calls / *_other steps get resampled (LLM cost paid) then silently dropped from the score card. Reproduced end-to-end on a smolagents-style trajectory. Details on agent_config.yaml.
  • H2 (structural) — CI still runs no pytest on pull_request, and this PR adds a mypy ignore_errors=true + ruff E722/F841/E402 carve-out for consistency_analyzer/*. Net: the vendored package now has neither static nor dynamic checks on a PR. Details on pyproject.toml. (Repo-wide issue, not solely this PR's to fix — but worth a CI job that runs the 164 new tests.)
  • M1format_trajectory_data discards the function name on non-JSON tool args (renders - call_7 instead of execute_sql(...)). Inline on consistency_guidelines.py.
  • M2pyyaml/numpy/pandas/scipy are imported by the pipeline but declared in none of [project.dependencies] (transitive-only). Inline on pyproject.toml.
  • M3n=samples provider assumption: a provider that returns 1 choice → empty score card → signal-less guidelines. Now warns (good) but still proceeds. Inline on inference_utils.py.
  • M4 (test gap) — the both-mode failure-isolation fix (the H above) has no test using side_effect=raise for consistency in both mode. The 11 new mode tests cover only happy-path dispatch and generation_method tagging. Worth one regression test asserting regular guidelines survive a consistency exception.
  • Low — thresholds in config/guidelines.py aren't range-validated (inline); single-sample distance = 2.0; dead always-None consistency_steps field; unguarded samples[0] in the numeric dispatch; resampling.py dict-path uses key-presence vs truthiness for tool_calls.

Claude's Verdict

Night-and-day from the first pass — the blockers are all resolved and verified. I'm marking request-changes for H1 (a real, reproducible correctness drop for non-OpenAI-schema tool-calling agents — exactly the smolagents case the e2e tests target) plus the medium items; none are large. Once agent_config.yaml covers the missing step names (or unmapped names log instead of dropping), and M1/M2 land, this is in good shape to merge.

Comment thread altk_evolve/llm/guidelines/consistency_analyzer/agent_config.yaml
Comment thread altk_evolve/llm/guidelines/consistency_guidelines.py
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py
Comment thread pyproject.toml
Comment thread pyproject.toml
Comment thread altk_evolve/config/guidelines.py Outdated

@gaodan-fang gaodan-fang left a comment

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.

Re-reviewed the current head against main. Requesting changes for four newly verified integration issues; each inline comment includes a minimal fix and regression-test shape. These are in addition to the still-open step-configuration gap, and I avoided duplicating the existing M1/M3 threads.

Comment thread altk_evolve/sync/phoenix_sync.py
Comment thread altk_evolve/llm/guidelines/consistency_guidelines.py
Comment thread altk_evolve/llm/guidelines/consistency_analyzer/inference_utils.py Outdated
Comment thread altk_evolve/frontend/mcp/mcp_server.py
evduester and others added 12 commits July 16, 2026 13:30
Two step types were being added to the IR, resampled at LLM cost, and
then silently dropped because agent_config.yaml has no entry for them:

1. Steps where _classify_step_response returns "other" (malformed or
   degenerate turns) — no meaningful resampling target exists.
2. AnyAgent tool_calls steps (tool_calls present but no OpenAI tools
   schema) — without the schema we cannot instruct the model to call
   tools, so any resample produces bogus results.

Skipping both at IR construction time ensures the IR only ever contains
OpenAIAgent_content, OpenAIAgent_tool_calls, and AnyAgent_content —
exactly the three entries in agent_config.yaml. No YAML changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ailure in format_trajectory_data

Previously a JSONDecodeError on tool call arguments fell back to just
the call id, dropping both the function name and the raw arg string —
the highest-signal tokens for a high-uncertainty step.

Now the inner try/except only wraps the JSON parsing; on failure it
falls back to the raw args string while still emitting the function
name. The outer KeyError handler (missing 'function' key) still falls
back to the call id as a last resort.

Strengthened the corresponding test to assert both name and raw args
appear in the output rather than just checking "Agent tool calls".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The consistency pipeline imports yaml (consistency_guidelines.py),
numpy (consistency_aggregator.py, consistency_metric.py), pandas and
scipy.stats (consistency_metric.py). These were only available
transitively via arize-phoenix and sentence-transformers — a transitive
dep drop would break consistency/both mode with an ImportError, caught
and logged silently in MCP, leaving the operator with no signal.

Declaring them explicitly makes the dependency durable. No install
overhead in practice since all four are already in the resolved set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…an 2 choices

Previously a provider that ignores n>1 triggered only a warning, then
returned 1 choice. Every downstream metric would hit its len(samples)<=1
guard, the score card would be empty, skip_on_no_uncertainty would
short-circuit, and zero guidelines would be produced after paying for
the full resampling budget — with only a single log line as evidence.

Now: if len(choices) < 2, raise EvolveException immediately (the
EvolveException re-raise bypasses the retry loop since retrying the
same provider won't produce more choices). In both mode this lets the
regular pipeline succeed and surfaces a real error to the operator.
The count-mismatch warning is kept for the case where we got at least 2
choices but fewer than requested.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions

- Remove E722 and F841 from consistency_analyzer per-file-ignores in pyproject.toml;
  all violations were genuine and are now fixed in sample_preprocessing.py
- Remove dead position-tracking variables (original_response, thought_pos,
  action_pos, action_input_pos, final_ans_pos) from sample_preprocessing.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sholds

Add Field(ge=0.0, le=1.0) to both threshold fields and a model_validator
that raises if low_uncertainty_threshold > high_uncertainty_threshold.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In "both" mode swallowing the exception is correct — regular guidelines
still get written and the trace is processed. In "consistency" mode there
are no regular guidelines, so swallowing causes the trajectory entity to be
written (marking the trace processed) with zero guidelines generated.
Re-raising lets the outer per-trace handler leave the trace unprocessed
and eligible for retry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…llback

phoenix_sync stores "unknown" when no model can be read from a span's
attributes. Since "unknown" is a non-empty string, `model or fallback`
would silently use it as a real model ID instead of falling back to
llm_settings.guidelines_model. Normalize it to None at the point of
ingestion so the fallback always applies.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck model

When the trajectory carries no model (or "unknown"), resampling falls back
to llm_settings.guidelines_model. Without the provider, litellm has to
guess the routing from the model name alone, which fails for providers that
require an explicit custom_llm_provider (e.g. watsonx).

Thread custom_llm_provider through resample_trajectory → get_response_sampling,
but only inject it for steps that use the configured fallback — for steps
with a per-trajectory model, let litellm infer the provider to avoid
misrouting a model from a different provider.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…od on UPDATE

resolve_conflicts() was restoring metadata only for ADD events, leaving UPDATE
events with {} metadata. This caused generation_method (and all other provenance)
to be silently wiped whenever a guideline entity was updated via conflict resolution.

Fix: for UPDATE, start from the old entity's metadata. Additionally, union any
generation_method values from incoming new entities that were not explicitly ADD'd
(i.e. those merged into the update) into a generation_methods list so both regular
and consistency provenance survive a merge.

Update the existing test that asserted UPDATE metadata == {} (documenting the bug),
and add two new tests: one verifying metadata preservation and one verifying the
generation_methods union.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@jayaramkr jayaramkr left a comment

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.

Re-reviewed against 05b70c4. This round resolved essentially everything from the last review, and I verified each fix by execution (not just reading the diff). Strong response — credit below. The remaining items are all new regressions introduced by the fixes themselves, not the original findings; they share a theme (the skip / normalize changes weren't propagated to every reader of the same data).

Last round's findings — all fixed ✅ (verified by re-running the repros)

Prior finding Status
B1 agent_config.yaml not packaged ✅ ships in the wheel
B2 .env ignored GuidelinesSettings; verified .env honored
B3 negative-cosine crash / B4 jaccard inversion ✅ both correct, no collateral
M1 function name dropped on non-JSON tool args ✅ name + raw args preserved (verified all 5 shapes)
M2 undeclared runtime deps numpy/pandas/pyyaml/scipy now in [project.dependencies]
M3 silent degradation on short choice count ✅ now raises EvolveException
H2 no pytest on PR + mypy/ruff carve-out check-code.yaml now runs pytest on pull_request; ruff ignore narrowed to just E402 (bare-except/unused-var re-enabled)
threshold validation ✅ out-of-range and high < low now rejected
both-mode failure isolation ✅ verified: consistency failure preserves regular guidelines; consistency-only re-raises
conflict-resolution UPDATE wiped metadata ✅ the metadata-preservation half is correct (and support-conservation on main is confirmed unaffected — consolidation bypasses resolve_conflicts)

Problems introduced by the new fixes (ranked)

Details inline. Summary:

  • CRITICAL — the skip-unscorable-steps fix makes IR step_number dense over scorable steps, but format_trajectory_data still numbers every assistant message positionally, so uncertainty markers land on the wrong step (even on skipped, never-scored steps). Silent misattribution — worse than the silent drop it replaced.
  • HIGH_can_segment_trajectory now over-approves (single-function_call list content), so segment_trajectory indices no longer map 1:1 to IR steps; valid subtasks get dropped and step_range is misapplied.
  • HIGH — the "normalize unknown model" fix is incomplete: transform_trajectory_to_IR re-reads the raw model and "unknown" survives, so resampling calls litellm.completion(model="unknown") → fails → no consistency guidelines for any phoenix trace lacking model attributes (the exact case the fix targeted).
  • MEDIUM-HIGH — the generation_method union pulls methods from unrelated/dropped new entities into every UPDATE (fabricated provenance).
  • MEDIUM — a mixed merge pop()s generation_method (the key every consumer + the both-mode e2e test read) and writes generation_methods (read by nothing but its own new test), so metadata.get("generation_method")None, violating test_e2e_mcp_consistency.py's contract.

The CRITICAL and the two HIGHs share one root cause — a change to how one producer numbers/normalizes steps wasn't mirrored in the other readers of that data — so they're a focused fix.

Comment thread altk_evolve/llm/guidelines/consistency_guidelines.py
Comment thread altk_evolve/llm/guidelines/consistency_guidelines.py
Comment thread altk_evolve/llm/guidelines/consistency_guidelines.py Outdated
Comment thread altk_evolve/llm/conflict_resolution/conflict_resolution.py Outdated
Comment thread altk_evolve/llm/conflict_resolution/conflict_resolution.py Outdated
…conflict metadata

CRITICAL: transform_trajectory_to_IR now advances step_number for every
assistant message (including skipped ones) so IR step numbers are positional
and stay in sync with format_trajectory_data's positional counting and
segment_trajectory's indices. Previously, dense (scorable-only) numbering
caused uncertainty markers to land on the wrong step in the summary sent
to the guideline LLM.

HIGH (same root): n_steps split into n_scorable_steps (guards/segmentation
minimum) and n_positional_steps (subtask range validation), so valid
subtasks whose end step falls on a skipped message are no longer dropped.

HIGH: strip the "unknown" model sentinel in transform_trajectory_to_IR
(trajectory.get("model") or None doesn't catch it since "unknown" is
truthy). The fix in generate_consistency_guidelines was bypassed because
transform_trajectory_to_IR re-read the raw model into each IR step's
llm_params, causing resampling to call litellm with model="unknown".

MEDIUM-HIGH + MEDIUM (conflict resolution): drop the generation_method
union that over-attributed provenance from unrelated/dropped entities to
every UPDATE in a batch. Keep only the metadata-preservation fix (seed
UPDATE metadata from the old entity's stored metadata). This restores the
scalar generation_method key that every consumer and e2e test reads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@jayaramkr jayaramkr left a comment

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.

Re-reviewed against ef80c5b. This round is a focused 54-line diff that resolves all five findings from the last review, and I verified each fix by execution in an isolated worktree (not just by reading the diff). Approving. Two non-blocking notes below.

Last round's findings — all fixed ✅ (re-ran each repro)

Prior finding Fix Verified
CRITICAL — uncertainty markers attach to the wrong step step_number += 1 now fires for every assistant message (including skipped ones), so IR numbering is positional and matches format_trajectory_data My original repro now flags Step 2 (the reasoning step), not Step 1 (the skipped tool call). New regression test test_skipped_step_advances_step_number covers both orderings
HIGH_can_segment_trajectory / segment↔IR mapping broken subtask ranges validated against n_positional_steps (count of all assistant turns) Under the guard, parse_openai_agents_trajectory's step count == n_positional_steps (3==3); indices line up
HIGH"unknown" model leaked past the fix into resampling sentinel stripped in transform_trajectory_to_IR (raw_model != "unknown") model="unknown" → IR llm_params.model=None → falls back correctly, so resampling no longer calls completion(model="unknown")
MEDIUM-HIGHgeneration_method union fabricates provenance union removed; UPDATE preserves the old entity's metadata (no cross-entity attribution) Confirmed — the metadata-preservation half is kept; no fabricated provenance
MEDIUM — writes unread generation_methods plural, breaks the both-mode e2e contract plural key gone; singular generation_method preserved test_e2e_mcp_consistency.py's generation_method in ("regular","consistency") assertion is satisfied again

Baselines in the worktree: 56 unit tests pass (consistency + conflict-resolution), ruff clean, mypy clean.

I also checked the changes don't introduce new problems: IR step_number is now non-contiguous (skips consume a number), but no consumer indexes by it — the aggregator iterates steps by list position and consistency_analysis.py:23 carries step.get("step_number", i) straight through to the score card, so the gaps are harmless. And the provenance under-attribution on UPDATE (a consistency entity merged into an existing regular one stays tagged regular) is the deliberate, documented simplification suggested last round — acceptable.

Two non-blocking notes

  1. Stale docstring_can_segment_trajectory still says segment indices "map 1:1 to IR step numbers." The real invariant is now positional-count alignment (the IR skips steps but still numbers them positionally). The code is correct; only the comment is imprecise.
  2. Parallel gap in the regular pipeline — this PR guards the consistency path (n_scorable_steps >= 2), but segment_trajectory in the regular pipeline still has no minimal-length guard, so a single-step trajectory can fan out into multiple near-identical segments there. Out of scope for this PR — filed separately as #293.

Nice work — the response to the last round was thorough and the fixes are clean. 🚀

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.

5 participants