Skip to content

initialize ruff and mypy - #51

Merged
illeatmyhat merged 6 commits into
mainfrom
bump-version
Feb 6, 2026
Merged

initialize ruff and mypy#51
illeatmyhat merged 6 commits into
mainfrom
bump-version

Conversation

@illeatmyhat

@illeatmyhat illeatmyhat commented Feb 6, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added client APIs to list namespaces and fetch all entities.
    • Demo server exposes simple arithmetic tools for interactive testing.
  • Bug Fixes / UX

    • CLI now consistently truncates and displays non-string content safely.
    • Helper scripts for loading/saving entities now handle invalid data more gracefully and print clearer errors.
  • Chores

    • CI and build pipelines updated to Python 3.12.
  • Documentation

    • Examples and docs updated to satisfy linters (no behavioral changes).

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@illeatmyhat has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

CI and tooling moved to Python 3.12 and mypy configured; backend interfaces extended (new search_namespaces) and filesystem backend refactored to a FilesystemNamespace model; KaizenClient gains namespace/entity listing helpers; assorted typing, lint, demo, plugin logging, docs, and small formatting updates across the repo.

Changes

Cohort / File(s) Summary
CI & Tooling
pyproject.toml, .github/workflows/check-code.yaml, .github/workflows/publish-pypi.yaml, .github/workflows/publish-testpypi.yaml
Upgrade CI/build/test matrices to Python 3.12; reorder/add dev deps and add [tool.mypy] config with per-module ignore_missing_imports overrides.
Backend core
kaizen/backend/base.py, kaizen/backend/filesystem.py, kaizen/backend/milvus.py
Add abstract search_namespaces to backend base; refactor filesystem backend to use new FilesystemNamespace Pydantic model (entities, next_id), update load/save/search/update/delete flows; add None-guard in Milvus namespace retrieval.
Frontend / Client / DB
kaizen/frontend/client/kaizen_client.py, kaizen/db/sqlite_manager.py
Annotate client backend as BaseEntityBackend; add all_namespaces and get_all_entities methods; minor API/formatting tweaks. SQLite manager: stricter None guards, parameter binding normalization, and lifecycle handling.
LLM: conflict resolution & tips
kaizen/llm/conflict_resolution/conflict_resolution.py, kaizen/llm/tips/tips.py
Tighter local typing, improved retry/error wrapping in resolve_conflicts, default prompt loading when none provided, and streamlined trajectory parsing/formatting.
CLI / MCP
kaizen/cli/cli.py, kaizen/frontend/mcp/__main__.py
Normalize content->string truncation in CLI outputs; adjust MCP module import path.
Demo & Examples
demo/filesystem/server.py, examples/low_code/*, examples/low_code/local_mcp_server.py
Demo server: widen parameter types, add TypedDicts, float size handling, and refactor internals; examples: lint/noqa additions, minor formatting; local MCP server: add two tool-exposed functions (add, multiply).
Plugins: entity scripts
plugins/kaizen/skills/learn/scripts/save_entities.py, plugins/kaizen/skills/recall/scripts/retrieve_entities.py
Add per-user plugin logging, file discovery, load/save helpers, merge logic, and lifecycle logging for entity scripts.
Docs, lint, small edits
CONFIGURATION.md, docs/LOW_CODE_TRACING.md, kaizen/auto/__init__.py, many examples, config files
Add # noqa: F401 to example kaizen.auto imports, unify string-quote style, minor formatting and doc example updates.
Tests & misc refactors
tests/*, extract_trajectories.py, kaizen/sync/*, kaizen/schema/exceptions.py, kaizen/sync/phoenix_sync.py, kaizen/utils/utils.py
Local variable renames in tests, added local type annotations and type:ignore comments, explicit pass in exceptions, whitespace/formatting changes. Review tests for changed assertion messages and renamed helpers.

Sequence Diagram(s)

sequenceDiagram
  participant Client as KaizenClient
  participant Backend as BaseEntityBackend
  participant FS as Filesystem (JSON)

  Client->>Backend: all_namespaces(limit)
  Backend->>FS: read namespace file(s)
  FS-->>Backend: JSON content
  Backend->>Backend: parse -> FilesystemNamespace model
  Backend-->>Client: list[Namespace]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • visahak

Poem

"I hopped through code with careful paws,
I changed some types and fixed some laws.
Namespaces counted, files tucked neat,
CI now runs on twelve — what a treat! 🥕"

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'initialize ruff and mypy' is vague and misleading—the PR primarily upgrades Python 3.10→3.12, adds type annotations, and refactors code, while mypy/ruff setup is only a minor configuration piece. Use a more accurate title reflecting the main changes, such as 'Upgrade to Python 3.12 and add comprehensive type annotations' or 'Upgrade Python version and enhance type safety across codebase'.
Docstring Coverage ⚠️ Warning Docstring coverage is 77.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bump-version

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 and usage tips.

@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

🤖 Fix all issues with AI agents
In `@pyproject.toml`:
- Line 3: Revert the manual change to the version field in pyproject.toml (the
line setting version = "0.2.0") so the file returns to the state before this
commit, and do not commit any manual version bumps; instead trigger the existing
GitHub release workflow (.github/workflows/release-github.yaml) with the
appropriate bump argument (e.g., bump-version: minor) so python-semantic-release
performs the version update, commit, and tag automatically.

Comment thread pyproject.toml Outdated
Update uv.lock

update version

version downgrade
@illeatmyhat illeatmyhat changed the title bump version to 0.2.0 initialize ruff and mypy Feb 6, 2026

@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 (4)
tests/unit/test_client.py (1)

146-158: ⚠️ Potential issue | 🟠 Major

test_get_all_entities is an exact duplicate of test_search_entities and tests the wrong method.

Both tests mock search_entities, call kaizen_client.search_entities(namespace_id="foobar", query="name") identically, and assert identical outcomes. The test_get_all_entities test should call kaizen_client.get_all_entities(namespace_id="foobar") instead, since get_all_entities internally delegates to search_entities with query=None.

kaizen/db/sqlite_manager.py (2)

52-75: ⚠️ Potential issue | 🟠 Major

Missing ROLLBACK when IntegrityError is caught.

Line 58 issues an explicit BEGIN. When IntegrityError is caught on line 69, the exception is re-raised as NamespaceAlreadyExistsException but the transaction opened by BEGIN is never rolled back. The next method that calls BEGIN on the same connection will fail with "cannot start a transaction within a transaction".

Proposed fix
             except sqlite3.IntegrityError as e:
+                self.connection.execute("ROLLBACK")
                 raise NamespaceAlreadyExistsException(f'Namespace "{namespace_id}" already exists.') from e

34-50: ⚠️ Potential issue | 🔴 Critical

Lock reuse causes deadlock; missing ROLLBACK on IntegrityError leaves transaction open.

Two issues in this class:

  1. Deadlock in reset(): Holds self._lock at line 124, then calls _create_namespace_table() at line 129, which immediately tries to acquire the same non-reentrant threading.Lock → deadlock. Extract the table-creation logic into an unlocked helper method (e.g., _create_namespace_table_unlocked()) called by both reset() and __enter__() while already holding the lock. Or switch to threading.RLock.

  2. Missing ROLLBACK in create_namespace(): When sqlite3.IntegrityError is caught at line 70, no ROLLBACK is executed. The BEGIN transaction from line 57 remains open. The next operation attempting BEGIN will fail with "cannot start a transaction within a transaction." Add self.connection.execute("ROLLBACK") in the IntegrityError handler before raising.

kaizen/frontend/client/kaizen_client.py (1)

39-41: ⚠️ Potential issue | 🟡 Minor

Incorrect docstring — copy-paste from get_namespace_details.

The docstring says "Get details about a specific namespace." but this method lists all namespaces.

Fix
     def all_namespaces(self, limit: int = 10) -> list[Namespace]:
-        """Get details about a specific namespace."""
+        """List all namespaces."""
         return self.backend.search_namespaces(limit)
🤖 Fix all issues with AI agents
In `@kaizen/sync/phoenix_sync.py`:
- Around line 188-200: The parsing logic in the block using self._parse_content
assumes every element has a "message" key which can raise KeyError and cause all
parsed outputs to be discarded; change the list comprehensions that build
output_msgs (the branch for parsed_output being a list and the branch for
parsed_output["choices"]) to safely filter and extract only items that actually
contain "message" (e.g., use a conditional comprehension or .get and check for
None) and preserve any valid messages; ensure the fallback still creates
output_msgs as [{"role": "assistant", "content": output_val}] when nothing valid
is found, and keep the existing exception logging via logger.debug with
self._format_payload_summary(output_val).
🧹 Nitpick comments (17)
.github/workflows/check-code.yaml (1)

1-1: Workflow name is now misleading.

The workflow is named "Check Formatting with Ruff" but it now covers lockfile validation, linting, formatting, and type-checking (mypy). Consider renaming to something broader, e.g., "Code Quality Checks".

Proposed fix
-name: Check Formatting with Ruff
+name: Code Quality Checks
examples/low_code/manual_phoenix_demo.py (2)

8-9: Relative path in load_dotenv is fragile.

"../../../.env" assumes the script is run from its own directory. If invoked from a different working directory, this silently won't load. This is acceptable for a demo, but consider using pathlib.Path(__file__).resolve().parents[3] / ".env" for robustness if this becomes a shared pattern.


71-72: Move import json to the top of the file.

Placing imports inside a loop body is unconventional. While Python caches modules so there's no runtime cost, moving it to the top-level imports (line 1–5) is more idiomatic.

Proposed fix

At the top of the file:

 import os
+import json
 from dotenv import load_dotenv

In the loop body:

                if tc.function.name == "add":
-                    import json
-
                     args = json.loads(tc.function.arguments)
kaizen/llm/tips/tips.py (1)

97-97: Nit: use a set literal for the membership test.

in ["action", "reasoning"] performs a linear scan; a set literal is both idiomatic and (marginally) faster.

Suggested change
-        "num_steps": len([s for s in agent_steps if s["type"] in ["action", "reasoning"]]),
+        "num_steps": len([s for s in agent_steps if s["type"] in {"action", "reasoning"}]),
plugins/kaizen/skills/recall/scripts/retrieve_entities.py (1)

38-55: Module-level logging executes at import time.

log("Script started") and the environment/argument logging blocks run unconditionally when the module is loaded, not just when invoked as __main__. If this file is ever imported as a library (e.g., for testing or reuse of load_entities), these side effects will fire. Consider moving them inside main() or guarding them with if __name__ == "__main__".

plugins/kaizen/skills/learn/scripts/save_entities.py (2)

41-42: Same module-level side effect concern as in retrieve_entities.py.

log("Script started") runs at import time. Since this script is primarily invoked as __main__, the practical risk is low, but it's worth guarding with if __name__ == "__main__" for hygiene — especially if find_entities_file or load_existing_entities are ever reused via import.


18-39: Extract duplicated _get_log_dir(), LOG_FILE, and log() into a shared logging module.

These three entities are duplicated verbatim across retrieve_entities.py and save_entities.py, differing only in the [retrieve] vs [save] log tag. Refactor them into plugins/kaizen/skills/_logging.py with the tag as a parameter to log(). This eliminates duplication and provides a single point for future changes to log format, rotation, or security hardening.

♻️ Sketch of a shared logging utility
# plugins/kaizen/skills/_logging.py
import datetime
import getpass
import os
import tempfile


def _get_log_dir():
    """Get user-scoped log directory with restrictive permissions."""
    try:
        uid = os.getuid()
    except AttributeError:
        uid = getpass.getuser()
    log_dir = os.path.join(tempfile.gettempdir(), f"kaizen-{uid}")
    os.makedirs(log_dir, mode=0o700, exist_ok=True)
    return log_dir


LOG_FILE = os.path.join(_get_log_dir(), "kaizen-plugin.log")


def log(message, tag="general"):
    """Append a timestamped message to the log file."""
    if not os.environ.get("KAIZEN_DEBUG"):
        return
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(f"[{timestamp}] [{tag}] {message}\n")

Then in each script:

from kaizen.skills._logging import log
# usage: log("Script started", tag="save")
kaizen/sync/phoenix_sync.py (2)

196-198: Remove the dead # pass comment.

This appears to be a leftover from a previous revision. It adds noise without value.

Proposed fix
                     else:
                         # Fallback for simple string output
                         output_msgs = [{"role": "assistant", "content": output_val}]
-                        # pass

206-229: Consider extracting the duplicated message-parsing loop into a helper.

The block at lines 158–182 (input messages) and lines 206–229 (output messages) are nearly identical — both parse individual message strings, extract role/content/tool_calls via dual-key lookup, and build the mapped message dict. The only difference is the "type" field ("prompt" vs "completion"). A small private method like _parse_message_list(raw_msgs, msg_type) would reduce duplication and make future key-mapping changes less error-prone.

examples/low_code/simple_openai.py (1)

19-19: Consider breaking this long line for readability.

This single-line call is quite wide (~120 characters). If Ruff's configured line length permits it, this is technically fine, but the previous multi-line form was arguably more readable for an example file meant to onboard users.

♻️ Suggested multi-line formatting
-        response = client.chat.completions.create(model=model, messages=[{"role": "user", "content": "What is 2 + 2?"}], max_tokens=10)
+        response = client.chat.completions.create(
+            model=model,
+            messages=[{"role": "user", "content": "What is 2 + 2?"}],
+            max_tokens=10,
+        )
kaizen/llm/conflict_resolution/conflict_resolution.py (1)

46-62: prompt_file variable is reused for two different template paths.

prompt_file is assigned on Line 52 (default conflict resolution prompt) and then reassigned on Line 60 (main conflict resolution template). While functionally fine since the first value is consumed immediately, it can be confusing during debugging. Consider a distinct name like default_prompt_file on Line 52.

♻️ Suggested rename for clarity
     if custom_update_entities_prompt is None:
-        prompt_file = Path(__file__).parent / "prompts/default_conflict_resolution.jinja2"
-        custom_update_entities_prompt = Template(prompt_file.read_text()).render()
+        default_prompt_file = Path(__file__).parent / "prompts/default_conflict_resolution.jinja2"
+        custom_update_entities_prompt = Template(default_prompt_file.read_text()).render()
kaizen/db/sqlite_manager.py (1)

112-118: delete_namespace lacks error handling around the transaction.

Unlike create_namespace and reset, delete_namespace has no try/except to ROLLBACK if the DELETE or COMMIT fails. While unlikely for a simple PK delete, it's inconsistent and leaves the connection in a broken transaction state on failure.

Proposed fix
     def delete_namespace(self, namespace_id: str):
         assert self._lock is not None
         assert self.connection is not None
         with self._lock:
-            self.connection.execute("BEGIN")
-            self.connection.execute("DELETE FROM namespaces WHERE id = ?", (namespace_id,))
-            self.connection.execute("COMMIT")
+            try:
+                self.connection.execute("BEGIN")
+                self.connection.execute("DELETE FROM namespaces WHERE id = ?", (namespace_id,))
+                self.connection.execute("COMMIT")
+            except Exception as e:
+                self.connection.execute("ROLLBACK")
+                logger.error(f"Failed to delete namespace: {e}")
+                raise
demo/filesystem/server.py (2)

375-387: Loop variable path shadows the function parameter.

Line 377 for path in entries: reuses the name of the function parameter (path on line 356). While valid_path has already captured the resolved value, this shadowing is confusing and would be flagged by linters like Ruff's A001/PLW1509 or Pylint.

Rename the loop variable
-    for path in entries:
-        entry_path = os.path.join(valid_path, path)
+    for entry_name in entries:
+        entry_path = os.path.join(valid_path, entry_name)
         is_dir = os.path.isdir(entry_path)
 
         try:
             stats = os.stat(entry_path)
             size = stats.st_size if not is_dir else 0
         except Exception:
             size = 0
 
-        detailed_entries.append({"name": path, "is_dir": is_dir, "size": size})
+        detailed_entries.append({"name": entry_name, "is_dir": is_dir, "size": size})

13-13: Inconsistent use of List[str] (typing) vs list[str] (built-in).

The file imports List from typing and uses it in function signatures (e.g., line 162 List[str] | None), but also uses the lowercase list[str] in type annotations (lines 373, 376). Since Python 3.9+ supports list[...] natively and this PR targets 3.12, consider replacing all List usages with the built-in list for consistency.

Also applies to: 162-162, 417-417, 493-493

kaizen/frontend/client/kaizen_client.py (1)

39-49: all_namespaces and search_namespaces are identical in implementation.

Both delegate to self.backend.search_namespaces(limit) with the same signature and default. Consider keeping only one, or differentiating their intent (e.g., search_namespaces could accept filter criteria in the future).

kaizen/backend/filesystem.py (2)

99-117: search_namespaces uses raw json.loads + dict access, inconsistent with other methods.

Every other data-loading path uses _load_namespace_data()FilesystemNamespace.model_validate(), but this method parses JSON manually and accesses data["id"], data["entities"], etc. directly. If the schema evolves (e.g., a field is renamed or gains a validator), this path won't benefit from the model.

Consider reusing FilesystemNamespace.model_validate(data) here, with the same try/except for fault tolerance:

Proposed fix
     def search_namespaces(self, limit: int = 10) -> list[Namespace]:
         """Search for namespaces."""
         namespaces = []
         with self._lock:
             for file_path in self.data_dir.glob("*.json"):
                 try:
-                    data = json.loads(file_path.read_text())
+                    data = FilesystemNamespace.model_validate(json.loads(file_path.read_text()))
                     namespaces.append(
                         Namespace(
-                            id=data["id"],
-                            created_at=datetime.datetime.fromisoformat(data["created_at"]),
-                            num_entities=len(data["entities"]),
+                            id=data.id,
+                            created_at=data.created_at,
+                            num_entities=len(data.entities),
                         )
                     )
-                except (json.JSONDecodeError, KeyError):
+                except (json.JSONDecodeError, KeyError, Exception):
                     continue

68-87: num_entities stored in JSON will be stale after entity mutations.

create_namespace sets num_entities=0 on line 83, but update_entities and delete_entity_by_id never update data.num_entities before calling _save_namespace_data. The stored value drifts from len(data.entities).

Currently safe because all readers recompute num_entities dynamically from len(data.entities), but the stale field in the JSON is misleading. Consider either dropping num_entities from FilesystemNamespace (since it's always derived) or updating it before save.

Comment thread kaizen/sync/phoenix_sync.py
@illeatmyhat
illeatmyhat merged commit 3d2c6e1 into main Feb 6, 2026
11 checks passed
@illeatmyhat
illeatmyhat deleted the bump-version branch February 6, 2026 20:45
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