chore: restore reverted test suites, folder structure, logic optimizations, and security patches#197
chore: restore reverted test suites, folder structure, logic optimizations, and security patches#197sheepdestroyer wants to merge 6 commits into
Conversation
…main.py optimizations
…port-time crashes during pytest collection
…and fix router/Dockerfile dependencies
Reviewer's GuideRestores the intended repo layout, async and Redis logic optimizations, token estimation heuristics, container/config security hardening, and CI wiring that were previously lost, while adding targeted tests and docs to guard against future regressions. Sequence diagram for async annotations caching in save_annotationssequenceDiagram
actor User
participant FastAPI_app as FastAPI_app
participant save_annotations as save_annotations
participant _read_annotations_async as _read_annotations_async
User->>FastAPI_app: POST /dashboard/save-annotations
FastAPI_app->>save_annotations: save_annotations(payload)
alt annotations_file_exists
save_annotations->>_read_annotations_async: _read_annotations_async(path)
_read_annotations_async->>_read_annotations_async: asyncio.to_thread(os.path.getmtime)
alt cache_miss_or_mtime_changed
_read_annotations_async->>_read_annotations_async: aiofiles.open(path)
_read_annotations_async->>_read_annotations_async: asyncio.to_thread(json.loads)
_read_annotations_async-->>_read_annotations_async: update _annotations_cache
end
_read_annotations_async->>_read_annotations_async: asyncio.to_thread(copy.deepcopy)
_read_annotations_async-->>save_annotations: existing_annotations
save_annotations->>save_annotations: merge new payload with existing_annotations
else no_existing_file
save_annotations->>save_annotations: write new annotations file
end
save_annotations-->>User: 200 OK
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (46)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a Git Rebase & Conflict Resolution Policy, updates the LiteLLM Gateway version pin to v1.90.2, replaces hardcoded credentials in pod.yaml with dynamic placeholders, and adds a robust regex-based token estimation heuristic in the router. It also migrates and expands the test suite with comprehensive unit and integration tests. However, two issues were identified in start-stack.sh: a missing comma in the Python placeholder list causes implicit string concatenation that will break deployment, and the removal of the early openssl check leads to a crash when generating POSTGRES_PASSWORD if openssl is not installed.
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.
| "MINIO_PASSWORD_PLACEHOLDER" | ||
| "LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER" |
There was a problem hiding this comment.
There is a missing comma at the end of line 401. In Python, consecutive string literals without a comma are implicitly concatenated, which will result in "MINIO_PASSWORD_PLACEHOLDERLANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER" being added to the placeholders list. This will cause the placeholder check to fail and the script to exit with an error during deployment.
| "MINIO_PASSWORD_PLACEHOLDER" | |
| "LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER" | |
| "MINIO_PASSWORD_PLACEHOLDER", | |
| "LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER" |
| # Ensure openssl is installed if we need to generate passwords/keys | ||
| if [ -z "$POSTGRES_PASSWORD" ] || [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ]; then | ||
| if ! command -v openssl &>/dev/null; then | ||
| echo "❌ Error: 'openssl' is required to generate secure random keys but was not found in PATH." | ||
| exit 1 | ||
| fi | ||
| fi | ||
|
|
||
|
|
There was a problem hiding this comment.
The early check for openssl was removed and moved down to line 98. However, POSTGRES_PASSWORD is generated using openssl at line 56, which is before the new check. If openssl is not installed, the script will crash with a command-not-found error at line 56 (due to set -e) instead of failing gracefully with the descriptive error message. The openssl check should be restored at the top of the script.
| # Ensure openssl is installed if we need to generate passwords/keys | |
| if [ -z "$POSTGRES_PASSWORD" ] || [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ]; then | |
| if ! command -v openssl &>/dev/null; then | |
| echo "❌ Error: 'openssl' is required to generate secure random keys but was not found in PATH." | |
| exit 1 | |
| fi | |
| fi | |
| # Ensure openssl is installed if we need to generate passwords/keys | |
| if [ -z "$POSTGRES_PASSWORD" ] || [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ] || [ -z "$LANGFUSE_INIT_USER_PASSWORD" ] || [ -z "$REDIS_AUTH" ] || [ -z "$CLICKHOUSE_PASSWORD" ] || [ -z "$ROUTER_API_KEY" ] || [ -z "$MINIO_ROOT_USER" ] || [ -z "$MINIO_ROOT_PASSWORD" ]; then | |
| if ! command -v openssl &>/dev/null; then | |
| echo "❌ Error: 'openssl' is required to generate secure random keys but was not found in PATH." | |
| exit 1 | |
| fi | |
| fi |
There was a problem hiding this comment.
Hey - I've found 1 security issue, 2 other issues, and left some high level feedback:
Security issues:
- Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Fixed security issues:
- Command injection from untrusted input passed to OS command execution (link)
General comments:
- In
start-stack.sh'srender_pod_yamlPython heredoc, theplaceholderslist is missing a comma between"MINIO_PASSWORD_PLACEHOLDER"and"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER", which will cause a syntax error and preventpod.yamlfrom being rendered correctly—add the comma to fix this.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `start-stack.sh`'s `render_pod_yaml` Python heredoc, the `placeholders` list is missing a comma between `"MINIO_PASSWORD_PLACEHOLDER"` and `"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER"`, which will cause a syntax error and prevent `pod.yaml` from being rendered correctly—add the comma to fix this.
## Individual Comments
### Comment 1
<location path="start-stack.sh" line_range="400-402" />
<code_context>
"ENCRYPTION_KEY_PLACEHOLDER",
- "postgres-password-***"
+ "postgres-password-***",
+ "MINIO_USER_PLACEHOLDER",
+ "MINIO_PASSWORD_PLACEHOLDER"
+ "LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER"
]
for ph in placeholders:
</code_context>
<issue_to_address>
**issue (bug_risk):** Missing comma between placeholder strings will cause a Python syntax error in the pod renderer.
In `render_pod_yaml`, the `placeholders` list is missing a comma between the last two entries:
```python
"MINIO_USER_PLACEHOLDER",
"MINIO_PASSWORD_PLACEHOLDER"
"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER"
```
This makes Python treat it as two elements: `"MINIO_USER_PLACEHOLDER"` and `"MINIO_PASSWORD_PLACEHOLDERLANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER"`, breaking the placeholder matching and potentially failing under stricter tooling. Please add a comma after `"MINIO_PASSWORD_PLACEHOLDER"`:
```python
"MINIO_USER_PLACEHOLDER",
"MINIO_PASSWORD_PLACEHOLDER",
"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER",
```
</issue_to_address>
### Comment 2
<location path="router/tests/test_memory_mcp.py" line_range="31-40" />
<code_context>
+# Tests from router/test_memory_mcp.py
+# =====================================================================
+
+def test_make_key_global():
+ """Test generating a key for global scope."""
+ category = "test_cat"
+ data = "test_data"
+
+ before_ts = int(time.time() * 1000)
+ key = _make_key(category, True, data)
+ after_ts = int(time.time() * 1000)
+
+ # Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}"
+ assert key.startswith(f"{PREFIX}:{SCOPE_GLOBAL}:{category}::")
+
+ # Extract timestamp and hash part
+ match = re.match(rf"^{PREFIX}:{SCOPE_GLOBAL}:{category}::(\d+):([a-f0-9]+)$", key)
+ assert match is not None, f"Key {key} does not match expected format"
+
+ ts = int(match.group(1))
+ h = match.group(2)
+
+ assert before_ts <= ts <= after_ts
+ assert len(h) == 20
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Extend _make_key tests to cover URL-encoding/decoding of categories and special characters.
Now that the category is URL-encoded via `urllib.parse.quote`, please add tests that use categories with spaces, colons, and non-ASCII characters (e.g. `"foo bar"`, `"cat:with:colons"`, `"日本語カテゴリ"`).
For each, assert that:
- `_make_key` produces a key where the category segment is percent-encoded.
- `_parse_key` (or equivalent) decodes the category back to the original value.
This will lock in the new quoting/unquoting behavior and catch regressions around special-character handling.
Suggested implementation:
```python
import pytest
import urllib.parse
from memory_mcp import (
PREFIX,
```
```python
def test_make_key_global():
"""Test generating a key for global scope."""
category = "test_cat"
data = "test_data"
before_ts = int(time.time() * 1000)
key = _make_key(category, True, data)
after_ts = int(time.time() * 1000)
# Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}"
assert key.startswith(f"{PREFIX}:{SCOPE_GLOBAL}:{category}::")
# Extract timestamp and hash part
match = re.match(rf"^{PREFIX}:{SCOPE_GLOBAL}:{category}::(\d+):([a-f0-9]+)$", key)
assert match is not None, f"Key {key} does not match expected format"
ts = int(match.group(1))
h = match.group(2)
assert before_ts <= ts <= after_ts
assert len(h) == 20
=======
# Tests from router/test_memory_mcp.py
# =====================================================================
def test_make_key_global():
"""Test generating a key for global scope."""
category = "test_cat"
data = "test_data"
before_ts = int(time.time() * 1000)
key = _make_key(category, True, data)
after_ts = int(time.time() * 1000)
# Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}"
assert key.startswith(f"{PREFIX}:{SCOPE_GLOBAL}:{category}::")
# Extract timestamp and hash part
match = re.match(rf"^{PREFIX}:{SCOPE_GLOBAL}:{category}::(\d+):([a-f0-9]+)$", key)
assert match is not None, f"Key {key} does not match expected format"
ts = int(match.group(1))
h = match.group(2)
assert before_ts <= ts <= after_ts
assert len(h) == 20
@pytest.mark.parametrize(
"category",
[
"foo bar",
"cat:with:colons",
"日本語カテゴリ",
],
)
def test_make_key_category_quoting_and_parsing(category):
"""Ensure categories with special characters are percent-encoded and round-trip via _parse_key."""
data = "test_data"
key = _make_key(category, True, data)
# Key format: f"{PREFIX}:{scope}:{quoted_category}::{ts}:{h}"
parts = key.split(":")
# PREFIX, scope, quoted_category, '', ts, h -> at least 6 parts
assert len(parts) >= 5
encoded_category = parts[2]
assert encoded_category == urllib.parse.quote(category, safe="")
# _parse_key should decode the category back to its original value
parsed = _parse_key(key)
# Assuming _parse_key returns a mapping with a "category" field.
assert parsed["category"] == category
```
The new test assumes that `_parse_key(key)` returns a mapping (e.g. `dict`) with a `"category"` key. If `_parse_key` instead returns a tuple, namedtuple, or dataclass, update the assertion line accordingly, for example:
- `assert parsed.category == category` (for a dataclass / namedtuple), or
- `assert parsed[2] == category` (if the third tuple element is the category).
Also, if `urllib.parse.quote` is used with a non-default `safe` parameter in `memory_mcp._make_key`, adjust the `safe` argument in `urllib.parse.quote(category, safe="")` to match that implementation.
</issue_to_address>
### Comment 3
<location path="scripts/get_pr_status.py" line_range="10" />
<code_context>
result = subprocess.run(argv, shell=False, capture_output=True, text=True, check=True, timeout=30)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
*Source: opengrep*
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| "MINIO_USER_PLACEHOLDER", | ||
| "MINIO_PASSWORD_PLACEHOLDER" | ||
| "LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER" |
There was a problem hiding this comment.
issue (bug_risk): Missing comma between placeholder strings will cause a Python syntax error in the pod renderer.
In render_pod_yaml, the placeholders list is missing a comma between the last two entries:
"MINIO_USER_PLACEHOLDER",
"MINIO_PASSWORD_PLACEHOLDER"
"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER"This makes Python treat it as two elements: "MINIO_USER_PLACEHOLDER" and "MINIO_PASSWORD_PLACEHOLDERLANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER", breaking the placeholder matching and potentially failing under stricter tooling. Please add a comma after "MINIO_PASSWORD_PLACEHOLDER":
"MINIO_USER_PLACEHOLDER",
"MINIO_PASSWORD_PLACEHOLDER",
"LANGFUSE_INIT_USER_PASSWORD_PLACEHOLDER",| def test_make_key_global(): | ||
| """Test generating a key for global scope.""" | ||
| category = "test_cat" | ||
| data = "test_data" | ||
|
|
||
| before_ts = int(time.time() * 1000) | ||
| key = _make_key(category, True, data) | ||
| after_ts = int(time.time() * 1000) | ||
|
|
||
| # Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}" |
There was a problem hiding this comment.
suggestion (testing): Extend _make_key tests to cover URL-encoding/decoding of categories and special characters.
Now that the category is URL-encoded via urllib.parse.quote, please add tests that use categories with spaces, colons, and non-ASCII characters (e.g. "foo bar", "cat:with:colons", "日本語カテゴリ").
For each, assert that:
_make_keyproduces a key where the category segment is percent-encoded._parse_key(or equivalent) decodes the category back to the original value.
This will lock in the new quoting/unquoting behavior and catch regressions around special-character handling.
Suggested implementation:
import pytest
import urllib.parse
from memory_mcp import (
PREFIX,def test_make_key_global():
"""Test generating a key for global scope."""
category = "test_cat"
data = "test_data"
before_ts = int(time.time() * 1000)
key = _make_key(category, True, data)
after_ts = int(time.time() * 1000)
# Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}"
assert key.startswith(f"{PREFIX}:{SCOPE_GLOBAL}:{category}::")
# Extract timestamp and hash part
match = re.match(rf"^{PREFIX}:{SCOPE_GLOBAL}:{category}::(\d+):([a-f0-9]+)$", key)
assert match is not None, f"Key {key} does not match expected format"
ts = int(match.group(1))
h = match.group(2)
assert before_ts <= ts <= after_ts
assert len(h) == 20
=======
# Tests from router/test_memory_mcp.py
# =====================================================================
def test_make_key_global():
"""Test generating a key for global scope."""
category = "test_cat"
data = "test_data"
before_ts = int(time.time() * 1000)
key = _make_key(category, True, data)
after_ts = int(time.time() * 1000)
# Expected format: f"{PREFIX}:{scope}:{category}::{ts}:{h}"
assert key.startswith(f"{PREFIX}:{SCOPE_GLOBAL}:{category}::")
# Extract timestamp and hash part
match = re.match(rf"^{PREFIX}:{SCOPE_GLOBAL}:{category}::(\d+):([a-f0-9]+)$", key)
assert match is not None, f"Key {key} does not match expected format"
ts = int(match.group(1))
h = match.group(2)
assert before_ts <= ts <= after_ts
assert len(h) == 20
@pytest.mark.parametrize(
"category",
[
"foo bar",
"cat:with:colons",
"日本語カテゴリ",
],
)
def test_make_key_category_quoting_and_parsing(category):
"""Ensure categories with special characters are percent-encoded and round-trip via _parse_key."""
data = "test_data"
key = _make_key(category, True, data)
# Key format: f"{PREFIX}:{scope}:{quoted_category}::{ts}:{h}"
parts = key.split(":")
# PREFIX, scope, quoted_category, '', ts, h -> at least 6 parts
assert len(parts) >= 5
encoded_category = parts[2]
assert encoded_category == urllib.parse.quote(category, safe="")
# _parse_key should decode the category back to its original value
parsed = _parse_key(key)
# Assuming _parse_key returns a mapping with a "category" field.
assert parsed["category"] == categoryThe new test assumes that _parse_key(key) returns a mapping (e.g. dict) with a "category" key. If _parse_key instead returns a tuple, namedtuple, or dataclass, update the assertion line accordingly, for example:
assert parsed.category == category(for a dataclass / namedtuple), orassert parsed[2] == category(if the third tuple element is the category).
Also, if urllib.parse.quote is used with a non-default safe parameter in memory_mcp._make_key, adjust the safe argument in urllib.parse.quote(category, safe="") to match that implementation.
|
|
||
| def run_cmd(argv: Sequence[str]) -> str: | ||
| """Runs a command and returns stripped stdout.""" | ||
| result = subprocess.run(argv, shell=False, capture_output=True, text=True, check=True, timeout=30) |
There was a problem hiding this comment.
security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
Source: opengrep
This PR comprehensively restores all files, directory organization, logic optimizations, container dependencies, and security patches that were mistakenly deleted or reverted during recent automated merge conflict resolutions on
master.1. Folder Structure & Reorganization Restored
tests/,scripts/,scripts/verification/, androuter/tests/) matching PR Refactor: Reorganize repository structure #181.router/test_memory_mcp.pyand consolidating tests inrouter/tests/test_memory_mcp.py).2. Missing Test and Utility Files Recovered
Checked out and restored the following 10 files from past commits in the repository history:
tests/test_read_annotations_async.py(PR ⚡ [performance improvement] Async annotations read #172 async annotations test suite)tests/test_get_pr_status.py(PR 🧪 [Add unit tests and implementation for get_pr_status.py] #193)router/tests/test_detect_active_tool.py(PR 🔒 Fix hardcoded default passwords in pod.yaml #187)router/tests/test_get_gemini_oauth_status.py(PR 🧪 Add tests for get_gemini_oauth_status #166)router/tests/test_get_goose_sessions.py(PR 🧪 Add missing tests for get_goose_sessions #165)router/tests/test_get_redis.py(PR 🧪 [Add URL support and test coverage for get_redis] #174)router/tests/test_load_persisted_stats.py(PR 🧪 [Add tests for load_persisted_stats] #167)scripts/benchmark_tokens.py(PR 🔒 Fix hardcoded default passwords in pod.yaml #187)tests/test_host_agy_daemon.py(PR Refactor: Reorganize repository structure #181/187)3. Logic Optimizations Re-applied in
router/main.py_read_annotations_asyncand deepcopy offloading) to prevent blocking the event loop._count_tokens_heuristicand updatedestimate_prompt_tokensto use it (bringing token estimation accuracy within ±11% of prose, code, and CJK).get_redisto handle client configuration properly.4. Security & Configuration Patches Restored
urllib.parse.quote(category)andunquote) inrouter/memory_mcp.pyto prevent parameter-injection vulnerabilities.pod.yamlandstart-stack.shback to secure, placeholder-based credential configurations (avoiding hardcoded default passwords in git).v1.90.2and newer container tags (ClickHouse, Valkey, pgvector, MinIO, and Langfuse).router/Dockerfileto includeaiofiles.5. CI Workflow & Test Collection Fixes
aiofilesto the pip install action in.github/workflows/test.yml.tests/andscripts/pathways.tests/test_agy_behavior.pyin a__main__block to prevent immediate execution at import-time during test collection (resolving CI collection crash).6. Agent Guidelines & Post-Mortem Documentation
.agents/AGENTS.mdto prevent automated bots from re-introducing folder and logic regressions..agents/jules_regression_analysis.md.Verification: All 174 unit and integration tests compile, collect, and pass successfully.
Summary by Sourcery
Restore previously reverted router logic, security-sensitive configuration, and test/CI infrastructure, and add guardrails to prevent future regressions.
Enhancements:
CI:
Deployment:
Documentation:
Tests:
Chores: