Skip to content

chore: address unresolved PR 208 reviews and fix doc inconsistencies#210

Closed
sheepdestroyer wants to merge 24 commits into
masterfrom
chore/pr208-review-fixes
Closed

chore: address unresolved PR 208 reviews and fix doc inconsistencies#210
sheepdestroyer wants to merge 24 commits into
masterfrom
chore/pr208-review-fixes

Conversation

@sheepdestroyer

@sheepdestroyer sheepdestroyer commented Jul 8, 2026

Copy link
Copy Markdown
Owner

This PR addresses all outstanding/unresolved reviews and comments from PR #208, and resolves multiple documentation inconsistencies identified in README.md.

Summary of Changes

  1. Security: Bound Valkey Cache to 127.0.0.1 (restricting external access under hostNetwork: true).
  2. Bug Fixes:
    • Converted _save_best_model_to_disk to synchronous (was async but scheduled via threadpool/to_thread).
    • Removed async threadpool wrapping for in-memory copy.deepcopy in annotations.
    • Propagated errors properly on seek/tell failures in agy_proxy.py quota checking.
    • Filtered memory MCP parser output to only return {data, tags}, dropping unexpected keys.
  3. Deployment: Target MinIO readiness check at the S3 API port (9002) at /minio/health/live instead of port 9001.
  4. Docs & Consistency:
    • Updated ClickHouse readiness query healthcheck credentials.
    • Synchronized Minio image tags and credentials in README.md.
    • Renamed test script to verify_reasoning_tiers.py and updated all links/paths.
    • Reorganized directory layout tree structure in README.md.
  5. Tests:
    • Added finite timeouts to urlopen and thread join in tests/test_host_agy_daemon.py to prevent CI hangs.
    • Added regression test for dropping extra fields in test_memory_mcp.py.

All 189 tests pass locally.

Summary by CodeRabbit

  • New Features

    • Added smarter prompt sizing, improved routing behavior, and support for newer memory key handling.
    • Expanded stack startup to auto-generate missing credentials and simplify first-time setup.
  • Bug Fixes

    • Improved handling of Redis/Valkey initialization, datetime serialization, and quota detection.
    • Fixed several routing, annotation loading, and free-model scoring behaviors for better reliability.
  • Documentation

    • Updated setup and testing guides to match the current project layout and recommended configuration.

…am limits and add early arg parsing for --help in start-stack.sh

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @sheepdestroyer, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR combines several independent changes: async improvements in router/main.py (regex token estimation, lazy AA-score loading, async AGY quota checks, cached annotation reads), memory_mcp v2 key format with concurrent deletion, placeholder-based secrets across pod.yaml and start-stack.sh, expanded/reorganized test suites, updated CI/dev scripts, agent policy docs, and a Prisma datetime serializer registration.

Changes

Deployment Secrets & Bootstrap Hardening

Layer / File(s) Summary
pod.yaml placeholder-based credentials and image bumps
pod.yaml
Hardcoded passwords/keys replaced with *_PLACEHOLDER tokens; LiteLLM gateway, ClickHouse, Postgres, Langfuse web/worker, and MinIO images bumped.
start-stack.sh secret generation and pod.yaml rendering
start-stack.sh
Adds UUID-based secret generation, interactive prompting, MinIO health-check updates, and reworked render_pod_yaml() placeholder validation.
README credential and version documentation
README.md
Documents generated ClickHouse/Minio credentials and version pinning in pod.yaml.

Router Async Behavior & Memory Key Improvements

Layer / File(s) Summary
Prompt token estimation and classifier truncation
router/main.py, router/tests/test_estimate_prompt_tokens.py, router/tests/test_routing_behavior.py
Regex-based weighted token estimator, VALKEY_URL fallback, classifier prompt truncation via CLASSIFIER_INPUT_MAX_CHARS.
Lazy AA-score loading and best-model persistence
router/main.py, tests/test_compute_free_model_score.py
AA scores loaded on-demand via asyncio.to_thread; _save_best_model_to_disk converted to sync.
Async AGY proxy quota detection and telemetry
router/agy_proxy.py, router/tests/test_agy_proxy.py, router/tests/test_routing_behavior.py
Async aiofiles-based quota log checks, proxy latency stats updates, llm-routing-agy model rewrite.
Async cached annotation reads
router/main.py, tests/test_read_annotations_async.py
Adds mtime-keyed annotation cache with async reader and deep-copy safety.
Memory MCP v2 keys and concurrent deletion
router/memory_mcp.py, router/tests/test_memory_mcp.py
memory:v2: URL-encoded key format, hardened value parsing, semaphore-bounded concurrent deletion.

Test Infrastructure and New Coverage

Layer / File(s) Summary
Shared conftest and import-path cleanup
tests/conftest.py, router/tests/conftest.py, tests/test_agy_behavior.py, tests/test_antigravity.py
Centralizes sys.path/CONFIG_PATH setup and guards against import-time side effects.
New router helper test suites
router/tests/test_detect_active_tool.py, ..._get_gemini_oauth_status.py, ..._get_goose_sessions.py, ..._get_redis.py, ..._load_persisted_stats.py
Adds standalone pytest coverage for multiple router helper functions.
Host AGY daemon end-to-end test suite
tests/test_host_agy_daemon.py
Adds threaded HTTP server tests covering streaming, timeouts, errors, and conversation-id flows.

Developer Scripts and CI Tooling

Layer / File(s) Summary
CI workflow, pytest config, and Dockerfile updates
.github/workflows/test.yml, pytest.ini, router/Dockerfile
Adds aiofiles, moves verification scripts under scripts//tests/ with explicit PYTHONPATH.
Concurrent classifier and token estimation benchmarks
scripts/benchmark_classifier.py, scripts/benchmark_tokens.py
Rate-limited concurrent classifier benchmarking; new token-estimation accuracy verification script.
Relocated get_pr_status CLI script and tests
scripts/get_pr_status.py, tests/test_get_pr_status.py
New gh-based PR status CLI with full test coverage.
Verification and watcher scripts
scripts/verification/verify_breaker.py, scripts/verification/verify_reasoning_tiers.py, scripts/watch_quota.sh
Adds tier-routing smoke test script and fixes path resolution.
Documentation of scripts and directory layout
scripts/README.md, README.md
Updates docs to reflect relocated tests and new scripts.

Agent Policy and Regression Analysis Notes

Layer / File(s) Summary
Agent policy and regression documentation
.agents/AGENTS.md, .agents/branch_audit_plan.md, .agents/jules_regression_analysis.md
Adds rebase/conflict-resolution policy and regression audit/analysis documents.

LiteLLM Prisma Datetime Serializer

Layer / File(s) Summary
Prisma datetime serializer registration
litellm/entrypoint.py
Registers a UTC/ISO-8601 datetime serializer for Prisma against original and patched datetime classes.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant StartStack as start-stack.sh
  participant EnvFile as .env
  participant PodYaml as pod.yaml

  Operator->>StartStack: run start-stack.sh
  StartStack->>StartStack: generate_uuid() for missing secrets
  StartStack->>EnvFile: write generated secrets (chmod 600)
  StartStack->>PodYaml: render_pod_yaml() substitutes *_PLACEHOLDER
  PodYaml-->>StartStack: validate no PLACEHOLDER tokens remain
Loading
sequenceDiagram
  participant Client
  participant MainRouter as router/main.py
  participant AgyProxy as agy_proxy.py
  participant CliLog as cli.log

  Client->>MainRouter: request routed to llm-routing-agy
  MainRouter->>AgyProxy: try_agy_proxy()
  AgyProxy->>AgyProxy: await _is_quota_exhausted()
  AgyProxy->>CliLog: aiofiles.open + seek/read last lines
  CliLog-->>AgyProxy: quota exhaustion markers
  AgyProxy-->>MainRouter: fallback / update proxy latency stats
Loading

Possibly related PRs

  • sheepdestroyer/LLM-Routing#118: Both PRs replace naive estimate_prompt_tokens logic with regex-based weighted heuristics and adjust the corresponding tests.
  • sheepdestroyer/LLM-Routing#156: Both PRs convert router/agy_proxy.py quota-exhaustion detection from sync open/readlines to async aiofiles-based reading with await propagation.
  • sheepdestroyer/LLM-Routing#184: Both PRs add the same Prisma datetime-serializer registration in litellm/entrypoint.py for datetime.datetime and RobustDatetime.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR's main purpose: resolving review feedback and fixing documentation inconsistencies.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/pr208-review-fixes

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.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@sheepdestroyer

Copy link
Copy Markdown
Owner Author

/gemini review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
.github/workflows/test.yml (1)

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

Pin aiofiles version for CI reproducibility.

httpx is pinned to 0.28.1 but aiofiles is unpinned. For consistency and reproducible CI builds, consider pinning it.

🤖 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 @.github/workflows/test.yml at line 26, The CI dependency install step is
leaving aiofiles unpinned, which makes builds less reproducible; update the test
workflow’s dependency list so aiofiles uses a fixed version like the other
pinned packages. Keep the change localized to the workflow install command in
the test job, alongside the existing httpx pin, so CI consistently installs the
same aiofiles release.
scripts/verification/verify_reasoning_tiers.py (1)

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

Consider narrowing broad exception catches.

Both except Exception blocks (lines 92 and 100) catch all exceptions including KeyboardInterrupt and SystemExit. For a verification script this is tolerable, but catching Exception explicitly (or narrowing to (KeyError, IndexError, httpx.HTTPError)) would avoid masking Ctrl+C during long runs.

Also applies to: 100-100

🤖 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 `@scripts/verification/verify_reasoning_tiers.py` at line 92, The exception
handling in verify_reasoning_tiers.py is too broad in the parse and HTTP/error
handling paths, so narrow the catches in the parse_inner and outer verification
blocks instead of using a blanket handler. Update the try/except around the
reasoning tier parsing and request verification logic to catch only the expected
failures (for example KeyError, IndexError, and httpx.HTTPError, or a similarly
scoped base exception), while avoiding interception of KeyboardInterrupt and
SystemExit.

Source: Linters/SAST tools

scripts/get_pr_status.py (1)

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

Consider catching subprocess.TimeoutExpired explicitly.

The generic Exception handler on line 48 catches TimeoutExpired (a SubprocessError subclass, not CalledProcessError), producing a less targeted message. Adding an explicit handler before the generic catch would give clearer feedback when gh hangs.

Proposed addition
     except subprocess.CalledProcessError as e:
         print(f"Error: Failed to fetch PR status: {e.stderr.strip()}", file=sys.stderr)
         sys.exit(1)
+    except subprocess.TimeoutExpired:
+        print("Error: gh CLI command timed out after 30 seconds", file=sys.stderr)
+        sys.exit(1)
     except json.JSONDecodeError:
🤖 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 `@scripts/get_pr_status.py` around lines 42 - 50, Add an explicit
subprocess.TimeoutExpired handler in get_pr_status.py before the generic except
Exception block so gh hangs are reported with a targeted timeout message instead
of the fallback error path. Update the exception handling around the subprocess
call in the PR status logic to catch TimeoutExpired separately, print a clear
timeout-specific stderr message, and exit with the same failure code while
leaving the existing CalledProcessError, JSONDecodeError, and generic Exception
handling intact.
start-stack.sh (2)

195-228: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add exit-status checks for direct openssl rand calls.

The generate_uuid() function (lines 155–164) properly validates openssl rand exit status and output length, but the direct openssl rand -hex calls for LANGFUSE_INIT_USER_PASSWORD, REDIS_AUTH, CLICKHOUSE_PASSWORD, ROUTER_API_KEY, MINIO_ROOT_USER, and MINIO_ROOT_PASSWORD do not. If openssl rand fails (e.g., entropy issues), empty secrets will be silently written to .env and used in production.

🔒 Proposed fix: add validation helper for direct openssl rand calls
+gen_hex() {
+    local val
+    val=$(openssl rand -hex "$1" 2>/dev/null)
+    local status=$?
+    if [ $status -ne 0 ] || [ -z "$val" ]; then
+        echo "❌ Error: Failed to generate secure random value (openssl rand exit $status)." >&2
+        return 1
+    fi
+    printf '%s' "$val"
+}
+
 if [ -z "$LANGFUSE_INIT_USER_PASSWORD" ]; then
-    LANGFUSE_INIT_USER_PASSWORD="$(openssl rand -hex 16)"
+    LANGFUSE_INIT_USER_PASSWORD="$(gen_hex 16)" || exit 1
     echo "LANGFUSE_INIT_USER_PASSWORD=\"$LANGFUSE_INIT_USER_PASSWORD\"" >> "$ENV_FILE"
     echo "✓ Generated new LANGFUSE_INIT_USER_PASSWORD and saved to $ENV_FILE"
 fi
 
 if [ -z "$REDIS_AUTH" ]; then
-    REDIS_AUTH="$(openssl rand -hex 16)"
+    REDIS_AUTH="$(gen_hex 16)" || exit 1
     echo "REDIS_AUTH=\"$REDIS_AUTH\"" >> "$ENV_FILE"

Apply the same pattern to CLICKHOUSE_PASSWORD, ROUTER_API_KEY (32), MINIO_ROOT_USER (4), and MINIO_ROOT_PASSWORD.

🤖 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 `@start-stack.sh` around lines 195 - 228, The secret-generation block in
start-stack.sh writes values from direct openssl rand -hex calls without
checking whether the command succeeded or returned output. Update the
assignments for LANGFUSE_INIT_USER_PASSWORD, REDIS_AUTH, CLICKHOUSE_PASSWORD,
ROUTER_API_KEY, MINIO_ROOT_USER, and MINIO_ROOT_PASSWORD to validate the openssl
result the same way generate_uuid() does, and only append to ENV_FILE after
confirming a non-empty value. Reuse or extract the existing validation pattern
so failures abort instead of silently persisting empty secrets.

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

Prefer direct exit-code check over $? (SC2181).

Shellcheck flags the $? check pattern after uuid=$(generate_uuid). Use if ! directly for clarity and to avoid masking intermediate command failures.

♻️ Proposed refactor
 if [ -z "$LANGFUSE_PUBLIC_KEY" ]; then
-    uuid=$(generate_uuid)
-    if [ $? -ne 0 ] || [ -z "$uuid" ]; then
+    if ! uuid=$(generate_uuid) || [ -z "$uuid" ]; then
         echo "❌ Error: Failed to generate LANGFUSE_PUBLIC_KEY." >&2
         exit 1
     fi

Apply the same pattern to LANGFUSE_SECRET_KEY (line 244).

Also applies to: 244-248

🤖 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 `@start-stack.sh` around lines 232 - 236, The uuid generation checks in the
LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY setup blocks use $? after
generate_uuid, which should be replaced with a direct exit-status check. Update
the conditional in the generate_uuid assignments to use a negated command check
and keep the empty-value guard, so failures are handled clearly without relying
on $?; apply the same change in both key-generation sections.

Source: Linters/SAST tools

router/tests/test_agy_proxy.py (1)

77-109: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add tests for the return False and seek/tell OSError paths.

The three new tests cover: log contains markers → True, throttled → True, file not found → True. Two meaningful paths are untested:

  1. Log read succeeds but no quota markers — this is the only path that returns False for the empty stdout/stderr case (source line 167). Without this test, a regression that always returns True when the log is readable would go undetected.
  2. seek/tell raises OSError — given the PR objective to propagate these errors, a test should verify the fallback behavior.
🧪 Suggested additional tests
+@patch("aiofiles.open")
+@patch("router.agy_proxy.time.time")
+@pytest.mark.asyncio
+async def test_is_quota_exhausted_empty_log_no_markers(mock_time, mock_open):
+    mock_time.return_value = 1000.0
+    mock_file = AsyncMock()
+    mock_file.seek = AsyncMock()
+    mock_file.tell = AsyncMock(return_value=100)
+    mock_file.read = AsyncMock(return_value=b"line 1\nsome other info\nline 3\n")
+    mock_open.return_value.__aenter__.return_value = mock_file
+    with patch("router.agy_proxy._last_log_check", 0):
+        assert await _is_quota_exhausted(0, "", "") is False
+
+@patch("aiofiles.open")
+@patch("router.agy_proxy.time.time")
+@pytest.mark.asyncio
+async def test_is_quota_exhausted_empty_seek_oserror(mock_time, mock_open):
+    mock_time.return_value = 1000.0
+    mock_file = AsyncMock()
+    mock_file.seek = AsyncMock(side_effect=OSError("seek failed"))
+    mock_file.tell = AsyncMock(return_value=100)
+    mock_open.return_value.__aenter__.return_value = mock_file
+    with patch("router.agy_proxy._last_log_check", 0):
+        # OSError swallowed by outer except → falls back to True
+        assert await _is_quota_exhausted(0, "", "") is True
🤖 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 `@router/tests/test_agy_proxy.py` around lines 77 - 109, Add coverage in the
existing _is_quota_exhausted test module for the missing branches: verify that
when aiofiles.open succeeds and the log content has no RESOURCE_EXHAUSTED
markers, _is_quota_exhausted returns False for empty stdout/stderr; also add a
test where the mocked file’s seek or tell raises OSError so the fallback
behavior is exercised. Use the existing _is_quota_exhausted helper and the
current aiofiles.open/time patches to keep the tests aligned with the current
async flow.
🤖 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 @.agents/branch_audit_plan.md:
- Around line 54-56: The verification summary in branch_audit_plan.md is stale
and still reports 181 passing tests; update the Verification Status section to
match the actual local run count of 189 passing tests. Locate the existing test
suite result under the Verification Status heading and refresh the tally so the
audit record reflects the current pytest outcome consistently.

In `@pod.yaml`:
- Around line 192-201: The ClickHouse readiness probe is still using an
unauthenticated query, while the container and livenessProbe already require
clickhouse credentials. Update the readinessProbe in pod.yaml to use
clickhouse-client with the same --user clickhouse and --password
CLICKHOUSE_PASSWORD_PLACEHOLDER arguments as the livenessProbe, and then keep
the README health-check table aligned with this authenticated readiness check.

In `@README.md`:
- Line 244: The README tree entry is inconsistent with the actual stack
configuration: update the documented directory name from valkey-lf-data/ to
redis-lf-data/ so it matches start-stack.sh and pod.yaml. Make the change in the
README’s directory layout section where the storage folder is listed, keeping
the rest of the tree description unchanged.
- Around line 580-590: The Health Check note still refers to the default
minioadmin keys, which conflicts with the updated Minio credential flow. Update
the Health Check section to reference the auto-generated credentials documented
in the Configuration section instead of minioadmin, and keep the wording aligned
with the env vars and placeholders used in README.md.

---

Nitpick comments:
In @.github/workflows/test.yml:
- Line 26: The CI dependency install step is leaving aiofiles unpinned, which
makes builds less reproducible; update the test workflow’s dependency list so
aiofiles uses a fixed version like the other pinned packages. Keep the change
localized to the workflow install command in the test job, alongside the
existing httpx pin, so CI consistently installs the same aiofiles release.

In `@router/tests/test_agy_proxy.py`:
- Around line 77-109: Add coverage in the existing _is_quota_exhausted test
module for the missing branches: verify that when aiofiles.open succeeds and the
log content has no RESOURCE_EXHAUSTED markers, _is_quota_exhausted returns False
for empty stdout/stderr; also add a test where the mocked file’s seek or tell
raises OSError so the fallback behavior is exercised. Use the existing
_is_quota_exhausted helper and the current aiofiles.open/time patches to keep
the tests aligned with the current async flow.

In `@scripts/get_pr_status.py`:
- Around line 42-50: Add an explicit subprocess.TimeoutExpired handler in
get_pr_status.py before the generic except Exception block so gh hangs are
reported with a targeted timeout message instead of the fallback error path.
Update the exception handling around the subprocess call in the PR status logic
to catch TimeoutExpired separately, print a clear timeout-specific stderr
message, and exit with the same failure code while leaving the existing
CalledProcessError, JSONDecodeError, and generic Exception handling intact.

In `@scripts/verification/verify_reasoning_tiers.py`:
- Line 92: The exception handling in verify_reasoning_tiers.py is too broad in
the parse and HTTP/error handling paths, so narrow the catches in the
parse_inner and outer verification blocks instead of using a blanket handler.
Update the try/except around the reasoning tier parsing and request verification
logic to catch only the expected failures (for example KeyError, IndexError, and
httpx.HTTPError, or a similarly scoped base exception), while avoiding
interception of KeyboardInterrupt and SystemExit.

In `@start-stack.sh`:
- Around line 195-228: The secret-generation block in start-stack.sh writes
values from direct openssl rand -hex calls without checking whether the command
succeeded or returned output. Update the assignments for
LANGFUSE_INIT_USER_PASSWORD, REDIS_AUTH, CLICKHOUSE_PASSWORD, ROUTER_API_KEY,
MINIO_ROOT_USER, and MINIO_ROOT_PASSWORD to validate the openssl result the same
way generate_uuid() does, and only append to ENV_FILE after confirming a
non-empty value. Reuse or extract the existing validation pattern so failures
abort instead of silently persisting empty secrets.
- Around line 232-236: The uuid generation checks in the LANGFUSE_PUBLIC_KEY and
LANGFUSE_SECRET_KEY setup blocks use $? after generate_uuid, which should be
replaced with a direct exit-status check. Update the conditional in the
generate_uuid assignments to use a negated command check and keep the
empty-value guard, so failures are handled clearly without relying on $?; apply
the same change in both key-generation sections.
🪄 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: 522b4b61-ad57-4bb4-8a1b-6d1ff1582fd9

📥 Commits

Reviewing files that changed from the base of the PR and between c304f3d and fb70eed.

⛔ Files ignored due to path filters (1)
  • debug-homepage.png is excluded by !**/*.png
📒 Files selected for processing (56)
  • .agents/AGENTS.md
  • .agents/branch_audit_plan.md
  • .agents/jules_regression_analysis.md
  • .github/workflows/test.yml
  • README.md
  • get_pr_status.py
  • litellm/entrypoint.py
  • pod.yaml
  • pytest.ini
  • router/Dockerfile
  • router/agy_proxy.py
  • router/main.py
  • router/memory_mcp.py
  • router/test_memory_mcp.py
  • router/tests/conftest.py
  • router/tests/test_agy_proxy.py
  • router/tests/test_detect_active_tool.py
  • router/tests/test_estimate_prompt_tokens.py
  • router/tests/test_get_gemini_oauth_status.py
  • router/tests/test_get_goose_sessions.py
  • router/tests/test_get_redis.py
  • router/tests/test_load_persisted_stats.py
  • router/tests/test_memory_mcp.py
  • router/tests/test_routing_behavior.py
  • scripts/README.md
  • scripts/benchmark_classifier.py
  • scripts/benchmark_tokens.py
  • scripts/get_pr_status.py
  • scripts/host_agy_daemon.py
  • scripts/sync_gemini_token.py
  • scripts/test_quota_reset.sh
  • scripts/verification/verify_breaker.py
  • scripts/verification/verify_reasoning_tiers.py
  • scripts/watch_quota.sh
  • start-stack.sh
  • test_memory_mcp.py
  • tests/conftest.py
  • tests/test_a2_verify.py
  • tests/test_agy_behavior.py
  • tests/test_agy_tiers.py
  • tests/test_antigravity.py
  • tests/test_atomic_write.py
  • tests/test_check_http_endpoint.py
  • tests/test_circuit_breaker.py
  • tests/test_classifier_accuracy.py
  • tests/test_compute_free_model_score.py
  • tests/test_get_pr_status.py
  • tests/test_host_agy_daemon.py
  • tests/test_map_tool_to_category.py
  • tests/test_models_proxy.py
  • tests/test_pie_chart_gradient.py
  • tests/test_read_annotations_async.py
  • tests/test_record_tool_usage.py
  • tests/test_src_badge.py
  • tests/test_stream_latency.py
  • tests/test_sync_gemini_token.py
💤 Files with no reviewable changes (6)
  • get_pr_status.py
  • tests/test_models_proxy.py
  • tests/test_circuit_breaker.py
  • router/test_memory_mcp.py
  • test_memory_mcp.py
  • tests/test_a2_verify.py

Comment on lines +54 to +56
## Verification Status
* **Test Suite:** Ran `PYTHONPATH=router:. pytest`
* **Result:** **181 tests passed** successfully (100% success rate).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the verification tally.
This report says 181 tests passed, but the PR objective says the local run completed with 189 passing tests. Please refresh the audit record so the verification section matches the actual run.

🤖 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 @.agents/branch_audit_plan.md around lines 54 - 56, The verification summary
in branch_audit_plan.md is stale and still reports 181 passing tests; update the
Verification Status section to match the actual local run count of 189 passing
tests. Locate the existing test suite result under the Verification Status
heading and refresh the tally so the audit record reflects the current pytest
outcome consistently.

Comment thread pod.yaml
Comment on lines +192 to +201
value: CLICKHOUSE_PASSWORD_PLACEHOLDER
image: docker.io/clickhouse/clickhouse-server:26.6.1.1193
livenessProbe:
exec:
command:
- clickhouse-client
- --user
- clickhouse
- --password
- clickhouse
- CLICKHOUSE_PASSWORD_PLACEHOLDER

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether ClickHouse 26.6.x allows unauthenticated access for the default user
# when CLICKHOUSE_USER and CLICKHOUSE_PASSWORD env vars are set.
# Check ClickHouse docs or test with:
# podman run --rm -e CLICKHOUSE_USER=clickhouse -e CLICKHOUSE_PASSWORD=test123 \
#   docker.io/clickhouse/clickhouse-server:26.6.1.1193 \
#   clickhouse-client --query "SELECT 1"
# vs
# podman run --rm -e CLICKHOUSE_USER=clickhouse -e CLICKHOUSE_PASSWORD=test123 \
#   docker.io/clickhouse/clickhouse-server:26.6.1.1193 \
#   clickhouse-client --user clickhouse --password test123 --query "SELECT 1"
echo "Verify ClickHouse readiness probe behavior with and without credentials"

Repository: sheepdestroyer/LLM-Routing

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant files and exact probe commands.
git ls-files | rg '(^|/)(pod\.yaml|README\.md)$'
echo '--- pod.yaml excerpt ---'
cat -n pod.yaml | sed -n '180,235p'
echo '--- README excerpt ---'
cat -n README.md | sed -n '70,95p'

Repository: sheepdestroyer/LLM-Routing

Length of output: 3881


Add credentials to the ClickHouse readiness probe
pod.yaml:208-213 still runs clickhouse-client --query "SELECT 1" without --user clickhouse --password CLICKHOUSE_PASSWORD_PLACEHOLDER, even though the container is configured with a generated password. Match the readiness probe to the liveness probe and update the README health-check table too.

🤖 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 `@pod.yaml` around lines 192 - 201, The ClickHouse readiness probe is still
using an unauthenticated query, while the container and livenessProbe already
require clickhouse credentials. Update the readinessProbe in pod.yaml to use
clickhouse-client with the same --user clickhouse and --password
CLICKHOUSE_PASSWORD_PLACEHOLDER arguments as the livenessProbe, and then keep
the README health-check table aligned with this authenticated readiness check.

Comment thread README.md
├── valkey-data/ # [Git Ignored] Persistent memory volumes for Valkey Cache
├── postgres-data/ # [Git Ignored] Persistent tables for PostgreSQL
├── clickhouse-data/ # [Git Ignored] Persistent traces for Langfuse v3
├── valkey-lf-data/ # [Git Ignored] Persistent job queues for Langfuse v3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Directory name mismatch: valkey-lf-data/ should be redis-lf-data/.

The directory layout tree documents valkey-lf-data/ but both start-stack.sh (line 40: mkdir -p ... redis-lf-data ...) and pod.yaml (line 439: path: .../redis-lf-data, name: redis-lf-storage) use redis-lf-data. This mismatch could confuse developers troubleshooting volume mount issues.

📝 Proposed fix
-├── valkey-lf-data/      # [Git Ignored] Persistent job queues for Langfuse v3
+├── redis-lf-data/       # [Git Ignored] Persistent job queues for Langfuse v3
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
├── valkey-lf-data/ # [Git Ignored] Persistent job queues for Langfuse v3
├── redis-lf-data/ # [Git Ignored] Persistent job queues for Langfuse v3
🤖 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 `@README.md` at line 244, The README tree entry is inconsistent with the actual
stack configuration: update the documented directory name from valkey-lf-data/
to redis-lf-data/ so it matches start-stack.sh and pod.yaml. Make the change in
the README’s directory layout section where the storage folder is listed,
keeping the rest of the tree description unchanged.

Comment thread README.md
Comment on lines +580 to +590
> Minio credentials are auto-generated by `start-stack.sh` and injected via `.env` placeholders. You **must not** hardcode credentials in `pod.yaml`.

| Env Var | Value |
|----------|-------|
| `LANGFUSE_S3_EVENT_UPLOAD_BUCKET` | `langfuse-events` |
| `LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT` | `http://127.0.0.1:9002` |
| `LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID` | `minioadmin` |
| `LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY` | `minioadmin` |
| `LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID` | `<generated>` (from `MINIO_USER_PLACEHOLDER`) |
| `LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY` | `<generated>` (from `MINIO_PASSWORD_PLACEHOLDER`) |
| `S3_FORCE_PATH_STYLE` | `true` |

Minio runs on ports **9001** (web console) and **9002** (S3 API). Default credentials (`minioadmin` / `minioadmin`) are only meant for local/dev setups. Image pinned to `docker.io/minio/minio:RELEASE.2025-10-15T17-29-55Z`.
Minio runs on ports **9001** (web console) and **9002** (S3 API). Credentials are generated at first boot and persisted in `.env`. Image pinned to `docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale minioadmin reference in Health Check section.

The Configuration section (lines 580–590) now correctly documents auto-generated credentials, but the Health Check section (line 597) still references "the default minioadmin keys described in Configuration." This is contradictory — the Configuration section no longer describes minioadmin defaults.

📝 Proposed fix: update the Health Check note
-> When deploying to staging or production, ensure that custom credentials are configured (rather than the default `minioadmin` keys described in [Configuration](`#configuration`)), and ensure the deployment's probes and S3 configurations are updated to reference the new values.
+> When deploying to staging or production, ensure that the auto-generated credentials from `start-stack.sh` are replaced with production-appropriate values, and that the deployment's probes and S3 configurations reference the correct credentials.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> Minio credentials are auto-generated by `start-stack.sh` and injected via `.env` placeholders. You **must not** hardcode credentials in `pod.yaml`.
| Env Var | Value |
|----------|-------|
| `LANGFUSE_S3_EVENT_UPLOAD_BUCKET` | `langfuse-events` |
| `LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT` | `http://127.0.0.1:9002` |
| `LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID` | `minioadmin` |
| `LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY` | `minioadmin` |
| `LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID` | `<generated>` (from `MINIO_USER_PLACEHOLDER`) |
| `LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY` | `<generated>` (from `MINIO_PASSWORD_PLACEHOLDER`) |
| `S3_FORCE_PATH_STYLE` | `true` |
Minio runs on ports **9001** (web console) and **9002** (S3 API). Default credentials (`minioadmin` / `minioadmin`) are only meant for local/dev setups. Image pinned to `docker.io/minio/minio:RELEASE.2025-10-15T17-29-55Z`.
Minio runs on ports **9001** (web console) and **9002** (S3 API). Credentials are generated at first boot and persisted in `.env`. Image pinned to `docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z`.
> When deploying to staging or production, ensure that the auto-generated credentials from `start-stack.sh` are replaced with production-appropriate values, and that the deployment's probes and S3 configurations reference the correct credentials.
🤖 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 `@README.md` around lines 580 - 590, The Health Check note still refers to the
default minioadmin keys, which conflicts with the updated Minio credential flow.
Update the Health Check section to reference the auto-generated credentials
documented in the Configuration section instead of minioadmin, and keep the
wording aligned with the env vars and placeholders used in README.md.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces comprehensive updates to the routing gateway, including security hardening via dynamic credential generation in start-stack.sh, image version upgrades, and restricting Valkey Cache to 127.0.0.1. It also optimizes performance by offloading blocking file operations to asynchronous threads using aiofiles and asyncio.to_thread, refactors token estimation with a regex-based heuristic, and implements robust URL-encoding for memory keys in memory_mcp.py. Additionally, the test suite has been reorganized and expanded with several new unit and integration tests. The code review feedback suggests further security hardening by restricting valkey-lf to bind only to 127.0.0.1 and adding defensive error handling when parsing the CLASSIFIER_INPUT_MAX_CHARS environment variable.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pod.yaml
Comment on lines 224 to +227
- --port
- '6380'
- --requirepass
- langfuse-redis-2026
- REDIS_AUTH_PLACEHOLDER

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-high high

Security: Since the pod is configured with hostNetwork: true, any container port binding to all interfaces (0.0.0.0) will be exposed to the external network. Just like the fix applied to valkey-cache, valkey-lf should be restricted to bind only to 127.0.0.1 to prevent unauthorized external access to the Langfuse queue.

    - --bind
    - 127.0.0.1
    - --port
    - '6380'
    - --requirepass
    - REDIS_AUTH_PLACEHOLDER

Comment thread router/main.py
try:
client = get_http_client()
# Truncate prompt to prevent 400 Bad Request in llama-server due to cache-ram limit
max_chars = int(os.getenv("CLASSIFIER_INPUT_MAX_CHARS", "300"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Defensive programming: os.getenv("CLASSIFIER_INPUT_MAX_CHARS") might not be a valid integer if misconfigured in the environment. It is safer to wrap this in a try-except block with a fallback default value.

            try:
                max_chars = int(os.getenv("CLASSIFIER_INPUT_MAX_CHARS", "300"))
            except ValueError:
                max_chars = 300

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.

1 participant