Skip to content

refactor: modularize runtime for library embedding and PyPI reuse#61

Merged
69gg merged 16 commits into
mainfrom
bugfix/clean-is-better
May 24, 2026
Merged

refactor: modularize runtime for library embedding and PyPI reuse#61
69gg merged 16 commits into
mainfrom
bugfix/clean-is-better

Conversation

@69gg

@69gg 69gg commented May 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Split monolithic runtime modules (ai/client, config, handlers, cognitive, memes, etc.) into subpackages with compatibility shims and lazy root re-exports for library use
  • Add Config.from_mapping / set_config, docs/python-api.md, and README library embed section; fix PACKAGE_ROOT skills path regression after client split
  • Dedupe meme helpers, clean injected comment spam, and expand configuration/development docs

Test plan

  • uv run ruff format . && uv run ruff check . && uv run mypy .
  • uv run pytest tests/test_public_api_imports.py tests/test_ai_client_setup_paths.py tests/test_config_from_mapping.py -v
  • Remote deploy verified: 75 tools load after restart, no post-restart ERRORs

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Embeddable Python library API (in-process config injection, stable package exports), Naga HTTP endpoints (auth/bind/send/unbind), attachment registry with rich-media rendering, multimodal analysis and meme tools, improved LLM client (queued requests, summarization, background calls).
  • Documentation

    • Expanded README and full Python API reference; updated configuration, deployment, development, architecture, and message-batching guides.
  • Behavior Changes

    • Strict non-parallel "end" tool semantics and retry/queue hardening; package/app version bumped to v3.5.0.
  • Chores

    • Added automated PR review configuration.

Review Change Stack

69gg and others added 9 commits May 23, 2026 18:23
Revert the parallel-tool prompt regression, clarify that end must run in
its own round after reading tool results, and defer end execution after
other tools succeed instead of skipping it and forcing another LLM turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
When end is bundled with other tools in one turn, run the other tools
normally but return an explicit rejection for end; clarify this in prompts
and each.md so models do not expect end to succeed in the same round.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep assistant plain-text in messages and use a generic retry hint instead of hardcoding send_message/end, avoiding misleading follow-up tool calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
Wire config_class as the canonical Config, slim loader to a compat shim,
and restore root lazy re-exports so library embed tests can inject config
without config.toml.

Co-authored-by: Cursor <cursoragent@cursor.com>
Extract ai, handlers, attachments, cognitive, memes, coordinator, onebot,
and agent runner into subpackages with compatibility shims; add python-api
docs and py.typed; trim noisy inline comments across runtime code.

Co-authored-by: Cursor <cursoragent@cursor.com>
Strip agent-generated label comments and duplicate section headers without
changing runtime behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared meme image utilities, consolidate ingest lock model,
and simplify memes API and bilibili WBI nav parsing.

Co-authored-by: Cursor <cursoragent@cursor.com>
The ai/client/setup.py module is one level deeper than the old client.py,
so Path(__file__).parents[1] pointed at Undefined/ai/ and loaded zero
builtin tools. Centralize package root resolution and add regression tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Export set_config from the root package, update python-api.md, and expand
README with a simpler core-feature bullet plus a Skills-focused embed example.

Co-authored-by: Cursor <cursoragent@cursor.com>
@69gg 69gg marked this pull request as ready for review May 23, 2026 14:38
Delete unreachable monolith files shadowed by subpackages, sync set_config
with ConfigManager, declare py.typed in the wheel, and add layout regressions.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d0360e84-36f6-4730-b20e-f4822a3f633d

📥 Commits

Reviewing files that changed from the base of the PR and between c85ad66 and d3cc973.

📒 Files selected for processing (1)
  • src/Undefined/__init__.py

📝 Walkthrough

Walkthrough

Large reorganization: AI client split into setup/queue/ask_loop; LLM logic moved to llm/*; new Config/ConfigBuilder, build_config, sectioned load_sections and env registry; attachments split into models/segments/render; Naga API routes added; prompt/tool end-call rules tightened; docs and packaging updated.

Changes

Core modularization and API/docs alignment

Layer / File(s) Summary
End-to-end refactor and additions
src/Undefined/ai/..., src/Undefined/ai/llm/..., src/Undefined/config/..., src/Undefined/attachments/..., src/Undefined/api/routes/naga/*, res/prompts/*, docs/*, ARCHITECTURE.md, .coderabbit.yaml, pyproject.toml
AI client split into client/setup, client/queue, client/ask_loop; new LLM subpackage (llm/requester, llm/sanitize, llm/streaming, llm/thinking, llm/types); config dataclass Config + ConfigBuilder, build_config, sectioned load_sections/*, env registry and ConfigManager.replace; attachments reorganized into models, segments, render, with registry/refactor; new Naga route modules (auth, bind, send, unbind) and package entry; prompt/tool rules tightened to forbid/handle parallel end; documentation and architecture updates; packaging/version bumps and root lazy re-exports added.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

A rabbit hops through refactors fast,
Queues align, prompts hold steady at last.
Configs assemble from builder seeds,
Attachments bloom where rendering leads.
Naga checks in—auth, bind, and send;
“End” waits its turn — then peace, my friend. 🐇✨

✨ 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 bugfix/clean-is-better

Configure base_branches to .* so CodeRabbit reviews PRs targeting any branch.

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

Note

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

Caution

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

⚠️ Outside diff range comments (1)
src/Undefined/api/routes/naga/send.py (1)

47-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject non-object JSON payloads early.

A non-dict JSON payload will crash on Line 52 (body.get(...)) and return 500 instead of a client error.

💡 Suggested fix
     try:
         body = await request.json()
     except Exception:
         return _json_error("Invalid JSON", status=400)
+    if not isinstance(body, dict):
+        return _json_error("JSON body must be an object", status=400)

     bind_uuid = str(body.get("bind_uuid", "") or "").strip()
🤖 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 `@src/Undefined/api/routes/naga/send.py` around lines 47 - 57, After parsing
request.json() in the route handler, validate that the returned body is a
dict/object before accessing body.get(...) and return a 400 via _json_error if
it is not; specifically, after "body = await request.json()" add a check for
"not isinstance(body, dict)" (or equivalent) and return _json_error("JSON
payload must be an object", status=400) to avoid the subsequent body.get(...)
calls (bind_uuid, naga_id, delivery_signature, uuid, target, message) from
raising an exception.
🟡 Minor comments (5)
docs/configuration.md-1087-1087 (1)

1087-1087: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix heading level jump in env registry section.

#### access jumps one level too deep from surrounding structure; this trips markdownlint and weakens TOC hierarchy.

🤖 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/configuration.md` at line 1087, The "access" heading currently uses one
too many #'s (shown as '#### access') which breaks the TOC and markdownlint;
change that heading to the correct level (e.g., '### access') in the env
registry section so it aligns with surrounding headings and restores proper
hierarchy—locate the heading text 'access' and replace the four-hash form with
the three-hash form to fix the level jump.
docs/python-api.md-103-105 (1)

103-105: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Specify fence language for the precedence block.

Add a language tag (e.g., text) to satisfy markdown linting.

Suggested patch
-```
+```text
 Python 显式 mapping / override  >  config.toml  >  环境变量  >  代码默认值
</details>

<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 @docs/python-api.md around lines 103 - 105, The fenced precedence line in
docs/python-api.md lacks a language tag; update the triple-backtick fence
surrounding the text "Python 显式 mapping / override > config.toml > 环境变量 >
代码默认值" to include a language specifier (e.g., change totext) so the code
block is properly tagged for markdown linting and rendering.


</details>

</blockquote></details>
<details>
<summary>docs/deployment.md-5-7 (1)</summary><blockquote>

`5-7`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Remove blank line inside the opening blockquote.**

The current quote block is split by an empty line, which triggers `MD028`.

 
<details>
<summary>Suggested patch</summary>

```diff
 > **作为 Python 库嵌入**:若你不需要启动 QQ Bot CLI,而是要在自己的应用或测试中复用 Undefined 组件(配置、`AIClient`、Skills、认知记忆等),请参阅 [Python 库 API 参考](python-api.md) 与 [配置详解 — 库嵌入配置](configuration.md#2-库嵌入配置)。CLI 入口(`Undefined` / `Undefined-webui`)行为不受库嵌入 API 影响。
- 
 > Python 版本要求:`3.11`~`3.13`(包含)。
```
</details>

<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 `@docs/deployment.md` around lines 5 - 7, The blockquote in docs/deployment.md
is split by an empty line causing MD028; remove the blank line between the two
blockquote lines so the quoted content ("作为 Python 库嵌入…CLI 入口(`Undefined` /
`Undefined-webui`)行为不受库嵌入 API 影响." and "Python 版本要求:`3.11`~`3.13`(包含)。") forms a
single continuous blockquote; simply join the lines so every quoted paragraph
starts with '>' with no intervening blank line.
```

</details>

</blockquote></details>
<details>
<summary>docs/configuration.md-59-61 (1)</summary><blockquote>

`59-61`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Add language tag to fenced code block.**

This fence should specify a language (e.g., `text`) to satisfy markdown linting and keep docs tooling consistent.

 
<details>
<summary>Suggested patch</summary>

```diff
-```
+```text
 Python 显式 mapping / builder.override  >  config.toml  >  环境变量  >  代码默认值
 ```
```
</details>

<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 @docs/configuration.md around lines 59 - 61, The fenced code block lacks a
language tag; update the markdown fence containing "Python 显式 mapping /
builder.override > config.toml > 环境变量 > 代码默认值" to include a language
(e.g., add "text" after the opening ```), so the block becomes a proper fenced
code block with a language tag for markdown linting and tooling consistency.


</details>

</blockquote></details>
<details>
<summary>docs/message-batching.md-87-90 (1)</summary><blockquote>

`87-90`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Fix relative links to source files from `docs/`.**

These links currently point to `docs/src/...` when rendered. They should be prefixed with `../` to resolve correctly.

 
<details>
<summary>Suggested patch</summary>

```diff
-- 实现:[src/Undefined/services/message_batcher/](src/Undefined/services/message_batcher/)
+- 实现:[src/Undefined/services/message_batcher/](../src/Undefined/services/message_batcher/)
@@
-- 创建/注入:[src/Undefined/handlers/message_flow.py](src/Undefined/handlers/message_flow.py)
+- 创建/注入:[src/Undefined/handlers/message_flow.py](../src/Undefined/handlers/message_flow.py)
```
</details>

<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 `@docs/message-batching.md` around lines 87 - 90, Update the broken relative
links in docs/message-batching.md so they point to the repository source (prefix
with ../); replace "src/Undefined/services/message_batcher/" with
"../src/Undefined/services/message_batcher/",
"src/Undefined/services/ai_coordinator.py" with
"../src/Undefined/services/ai_coordinator.py" (which references functions
handle_auto_reply / handle_private_reply / _dispatch_grouped_request),
"src/Undefined/handlers/message_flow.py" with
"../src/Undefined/handlers/message_flow.py", and "src/Undefined/main.py" with
"../src/Undefined/main.py" so the links resolve correctly when rendered from
docs/.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>docs/configuration.md (1)</summary><blockquote>

`185-185`: _⚡ Quick win_

**Section numbering is out of sync after inserting new section 2.**

Several headers still use old numbering (e.g., `### 4.1` under `## 5`), which makes anchors and cross-references confusing.

 
<details>
<summary>Suggested patch</summary>

```diff
-### 4.1 `[core]` 机器人核心行为
+### 5.1 `[core]` 机器人核心行为
```

```diff
-### 5.1 热更新监听对象
+### 6.1 热更新监听对象
```

```diff
-### 5.2 明确“需重启”的字段
+### 6.2 明确“需重启”的字段
```

```diff
-### 5.3 明确“会执行热应用”的字段
+### 6.3 明确“会执行热应用”的字段
```

```diff
-### 5.4 其他字段
+### 6.4 其他字段
```

(And continue this renumbering pattern for subsequent subsections.)
</details>


Also applies to: 1012-1012, 1065-1065

<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 `@docs/configuration.md` at line 185, Section numbering is out of sync: update
the misplaced header "### 4.1 `[core]` 机器人核心行为" (currently under "## 5") and any
other headers like those at the noted locations (e.g., lines referenced 1012 and
1065) so that subsection numbers follow the parent section number (renumber "###
4.1" to "### 5.1" and continue the same renumbering pattern for all subsequent
subsections and related anchors/cross-references); ensure any internal links or
table of contents entries that reference the old numbers are updated to the new
numbers to keep anchors consistent.
```

</details>

</blockquote></details>
<details>
<summary>src/Undefined/ai/multimodal/analyzer.py (1)</summary><blockquote>

`19-20`: _⚡ Quick win_

**No runtime break: `Undefined.ai.multimodal.__init__` aliases `_MEDIA_URL_*` constants before loading `analyzer`.**
The analyzer’s `_multimodal_pkg._MEDIA_URL_*` lookups won’t raise `AttributeError` because `__init__.py` explicitly re-exports `_MEDIA_URL_CACHE_DIR`, `_MEDIA_URL_CACHE_TTL_SECONDS`, `_MEDIA_URL_CACHE_MAX_FILES`, `_MEDIA_URL_CACHE_CLEANUP_INTERVAL_SECONDS`, `_MEDIA_URL_DOWNLOAD_TIMEOUT_SECONDS`, and `_MEDIA_URL_DOWNLOAD_TMP_SUFFIX` from `Undefined.ai.multimodal.constants` (and includes them in `__all__`) before importing `MultimodalAnalyzer`. Optional: direct-importing these from `constants.py` could reduce indirection.

<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 `@src/Undefined/ai/multimodal/analyzer.py` around lines 19 - 20, The analyzer
currently relies on Undefined.ai.multimodal (referenced as _multimodal_pkg) to
re-export internal constants like _MEDIA_URL_CACHE_DIR,
_MEDIA_URL_CACHE_TTL_SECONDS, _MEDIA_URL_CACHE_MAX_FILES,
_MEDIA_URL_CACHE_CLEANUP_INTERVAL_SECONDS, _MEDIA_URL_DOWNLOAD_TIMEOUT_SECONDS
and _MEDIA_URL_DOWNLOAD_TMP_SUFFIX before loading MultimodalAnalyzer; to remove
this indirection and avoid fragile aliasing, import those constants directly
from Undefined.ai.multimodal.constants (or replace uses of
_multimodal_pkg._MEDIA_URL_* with direct references to the constants module) in
analyzer.py so MultimodalAnalyzer reads the values straight from the source
(keep references to _multimodal_pkg and MultimodalAnalyzer only where needed).
```

</details>

</blockquote></details>
<details>
<summary>src/Undefined/config/parsers/helpers.py (1)</summary><blockquote>

`24-31`: _⚡ Quick win_

**Preserve admin order when deduplicating IDs**

Line 27 uses `set`, which can reorder admin IDs and make downstream behavior/log output unstable. Prefer order-preserving dedupe.

 

<details>
<summary>Proposed change</summary>

```diff
 def _merge_admins(superadmin_qq: int, admin_qqs: list[int]) -> tuple[int, list[int]]:
     # admins.json 与 config.toml 的 admin_qq 合并,去重后超管必在列表中
     local_admins = load_local_admins()
-    all_admins = list(set(admin_qqs + local_admins))
+    all_admins = list(dict.fromkeys([*admin_qqs, *local_admins]))
     if superadmin_qq and superadmin_qq not in all_admins:
         all_admins.append(superadmin_qq)
     # 校验必填字段
     return superadmin_qq, all_admins
```
</details>

<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 `@src/Undefined/config/parsers/helpers.py` around lines 24 - 31, The
_merge_admins function currently uses set() to deduplicate admin_qqs +
local_admins which can reorder IDs; replace the set-based dedupe with an
order-preserving approach (e.g., iterate admin_qqs then local_admins and build a
new list using a seen set or dict.fromkeys on the concatenated sequence) so that
all_admins preserves original order while removing duplicates, and still ensure
superadmin_qq is appended if present and missing.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with 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.

Inline comments:
In @src/Undefined/ai/client/queue.py:

  • Line 109: The code registers a pending entry in _pending_llm_calls keyed by
    request_id before calling add_queued_llm_request, which can fail and leave a
    stale entry; wrap the call to add_queued_llm_request in a try/except (or move
    the registration until after a successful enqueue) and ensure any exception
    removes the entry (pop request_id from _pending_llm_calls) and re-raises the
    error; apply the same cleanup pattern for the block that currently relies on the
    later pop (i.e., the code paths around add_queued_llm_request and the later
    removal) so no pending entries leak on enqueue failure.

In @src/Undefined/ai/client/setup.py:

  • Around line 418-425: apply_intro_config currently launches
    _refresh_intro_generator as a detached asyncio task which can outlive shutdown;
    change it to store and track the task (e.g., self._intro_refresh_task or add to
    a background tasks set like self._bg_tasks) instead of dropping the handle,
    cancel any existing tracked task before creating a new one, attach a done
    callback that removes the task from tracking and logs exceptions, and ensure
    close() awaits or cancels and awaits this tracked task so
    _refresh_intro_generator cannot recreate resources after teardown (update
    apply_intro_config, _refresh_intro_generator tracking usage, and close()
    accordingly).
  • Around line 744-756: In request_model() the merged tool list from
    tool_manager.maybe_merge_agent_tools(...) is never passed through
    _filter_tools_for_runtime_config(), so runtime-hidden tools (e.g.,
    naga_code_analysis_agent) can still be sent upstream; after merging (assign to
    tools) and before any upstream use or the _maybe_prefetch_tools call, run the
    tools through self._filter_tools_for_runtime_config(tools, call_type) (await it
    if it's async) and assign the result back to tools so the filtered list is used
    thereafter (affecting subsequent _maybe_prefetch_tools and any transmission).

In @src/Undefined/ai/llm/requester.py:

  • Around line 158-182: The prompt cache scope builder currently places raw
    stable identifiers into the cache key (see _build_scope_prompt_cache_part used
    by _build_default_prompt_cache_key), which leaks user/group/sender IDs to
    upstream APIs; change _build_scope_prompt_cache_part to emit an opaque token
    instead of plaintext ids by hashing/encoding the id (e.g., run the numeric id
    through the existing _hash8 or an HMAC with a server-side secret) and return
    e.g. "group:{hash}", "private:{hash}", "sender:{hash}" (leave the global and
    request_type branches unchanged) so the key still partitions correctly but no
    raw identifiers are exposed.

In @src/Undefined/ai/llm/streaming.py:

  • Around line 357-383: aggregate_responses_stream currently sets final_response
    for any event that carries a dict "response", which causes non-terminal events
    to override accumulated output and skip synthesis from
    output_text_parts/output_items; to fix, remove the early unconditional
    assignment "if isinstance(response, dict): final_response = response" and only
    assign final_response inside the "if event_type == 'response.completed'" branch
    (i.e., set final_response = response there), then keep the existing usage-merge
    logic (checking usage and final_response.get("usage")) and allow the fallback
    synthesis from output_text_parts/output_items when final_response remains None;
    reference symbols: aggregate_responses_stream, final_response, response,
    event_type, output_text_parts, output_items, usage.

In @src/Undefined/ai/multimodal/detection.py:

  • Around line 31-42: The _get_media_type_by_extension function is too permissive
    because it checks "if ext in url_lower" across the whole URL; instead extract
    the URL path (e.g., via urllib.parse.urlparse) or derive the filename
    (os.path.basename) and then match the file extension exactly (use
    os.path.splitext or check path.lower().endswith(extension) with a leading dot)
    against IMAGE_EXTENSIONS, AUDIO_EXTENSIONS, and VIDEO_EXTENSIONS; update the
    function to operate on the parsed path/filename and perform exact extension
    comparisons so filenames like "file.png" map to "image" and substrings elsewhere
    in the URL no longer cause misclassification (leave the default return "image"
    if no extension matches).

In @src/Undefined/ai/multimodal/parsing.py:

  • Around line 11-14: _pars e_line_value currently splits generically on ":" and
    ":" which truncates values containing colons; change it to locate the exact
    prefix in the line (use line.find(prefix)), compute the slice start as the index
    after the matched prefix, then if the next character is a full‑width or ASCII
    colon skip that single character and return the rest stripped (return "" if the
    stripped result == "无"); apply the same fix to the other occurrence noted (the
    identical parsing use at line ~37) so both places use prefix-based slicing
    instead of generic split.

In @src/Undefined/ai/prompts/system_context.py:

  • Around line 23-27: The code currently returns a hardcoded
    "res/prompts/undefined.xml" when NagaAgent mode is off, ignoring the function's
    default_path parameter; update the branch that handles enabled == False to
    return the provided default_path argument (or fall back to default_path if the
    runtime_config lookup fails) instead of the hardcoded string so callers of the
    function (who pass default_path) get the intended path; locate the logic around
    the nagaagent_mode_enabled check (the enabled variable and the if enabled: ...
    return "res/prompts/undefined_nagaagent.xml") and change the final return to use
    default_path.

In @src/Undefined/api/routes/naga/bind.py:

  • Around line 40-47: After parsing request.json() in the route handler, validate
    that the result is a mapping/object before extracting fields: check
    isinstance(body, dict) (or collections.abc.Mapping) and if not return
    _json_error("Invalid JSON", status=400); then safely pull bind_uuid, naga_id,
    status as before. Also ensure you handle None values (treat as empty strings) so
    AttributeError won't occur when accessing .get on a non-dict.

In @src/Undefined/api/routes/naga/unbind.py:

  • Around line 37-44: The handler currently assumes request.json() returns a dict
    and calling body.get(...) can raise AttributeError when the JSON payload is not
    an object; after awaiting request.json() (in the same block where body is
    assigned) add a type check (e.g., isinstance(body, dict)) and if it is not a
    dict return _json_error("Invalid JSON", status=400); then proceed to extract
    bind_uuid, naga_id, and delivery_signature as currently done. Ensure this
    validation is applied in the same scope where body is used so _json_error is
    returned for non-object JSON instead of letting body.get raise.

In @src/Undefined/attachments/render.py:

  • Around line 151-160: The current logic unconditionally converts
    record.local_path to a file:// URI even if the file is missing, which can
    override a valid record.source_ref; update the branch that sets image_source to
    Path(record.local_path).resolve().as_uri() to first check that record.local_path
    points to an existing file (e.g., Path(record.local_path).exists() or is_file())
    and only then set image_source from the local path; if the local file is
    missing, fall back to using record.source_ref (and only emit the missing-file
    replacement + raise AttachmentRenderError / append to
    delivery_parts/history_parts when neither local file nor source_ref is
    available), keeping existing behavior for strict, uid, delivery_parts,
    history_parts, and AttachmentRenderError.
  • Around line 261-272: The current code treats any non-"group" target_type as a
    private send (calling sender.send_private_file), which can deliver files to the
    wrong channel; change the conditional in the attachments/render.py block that
    uses target_type/target_id/send_record so it explicitly handles "group" and
    "private" (calling sender.send_group_file or sender.send_private_file
    respectively) and otherwise skips sending and logs or records an unsupported
    target_type error (include target_type and target_id in the log) so unknown
    types are not silently sent as private.

In @src/Undefined/config/load_sections/finalize.py:

  • Around line 26-34: Guard the debug logging call so missing keys don't raise
    KeyError when strict=False: before calling _log_debug_info in finalize.py, check
    the strict flag or verify each context key exists (e.g.,
    "chat_model","vision_model","security_model","naga_model","agent_model","summary_model","grok_model")
    and only pass present values (or skip the call entirely) so _log_debug_info is
    invoked only when those ctx keys exist; update the call site that currently does
    _log_debug_info(ctx["chat_model"], ...) to use conditional presence checks or
    ctx.get(...) and short-circuit when strict is False.

In @src/Undefined/config/load_sections/history_skills.py:

  • Around line 192-203: The hot-reload timing values returned by
    skills_hot_reload_interval and skills_hot_reload_debounce are unbounded and may
    be non-positive; after obtaining and coercing each value via _get_value and
    _coerce_float, clamp or normalize them to sensible positive minimums (e.g.,
    enforce interval >= 0.1s and debounce >= 0.01s) before returning or storing so
    watcher scheduling cannot be destabilized; update the handling around the
    skills_hot_reload_interval and skills_hot_reload_debounce assignments to apply
    this min-value check.

In @src/Undefined/config/load_sections/integrations.py:

  • Around line 152-159: The code reads integer config values via
    _get_value/_coerce_int (e.g., code_delivery_command_timeout and
    code_delivery_max_command_output) but doesn't enforce lower bounds, allowing
    zero/negative values; update the parsing to validate and clamp values to
    sensible minima (for example, command timeout >= 1 second, max output >= 1 char)
    and fall back to the existing defaults when values are missing or invalid; apply
    the same lower-bound guards to the other code delivery fields in the same block
    (the variables parsed around lines 166-178) so archive/size/retry counts cannot
    be zero/negative and use defaults when out of range.

In @src/Undefined/config/load_sections/knowledge.py:

  • Around line 60-64: The code currently only enforces knowledge_chunk_overlap >=
    0; change the validation after computing knowledge_chunk_overlap (the block
    using _coerce_int and _get_value) to ensure overlap is strictly less than
    knowledge_chunk_size by capping it to at most max(knowledge_chunk_size - 1, 0)
    (use the existing knowledge_chunk_size value from config) so that downstream
    chunk stepping remains positive; update the block that sets
    knowledge_chunk_overlap to also check knowledge_chunk_size and reduce overlap
    when knowledge_chunk_overlap >= knowledge_chunk_size.

In @src/Undefined/config/load_sections/logging_tools.py:

  • Around line 37-42: log_max_size_mb and log_backup_count are currently taken
    directly from _get_value/_coerce_int and can be non-positive or negative; clamp
    them to safe bounds after coercion: for log_max_size_mb call
    _coerce_int/_get_value as today but then replace with something like
    log_max_size_mb = max(1, log_max_size_mb) to ensure rotation stays enabled, and
    for log_backup_count use log_backup_count = max(0, log_backup_count) to prevent
    negative backups; update the assignments around the existing symbols
    log_max_size_mb and log_backup_count (which call _coerce_int/_get_value) to
    perform these min/max clamps immediately after coercion.

Outside diff comments:
In @src/Undefined/api/routes/naga/send.py:

  • Around line 47-57: After parsing request.json() in the route handler, validate
    that the returned body is a dict/object before accessing body.get(...) and
    return a 400 via _json_error if it is not; specifically, after "body = await
    request.json()" add a check for "not isinstance(body, dict)" (or equivalent) and
    return _json_error("JSON payload must be an object", status=400) to avoid the
    subsequent body.get(...) calls (bind_uuid, naga_id, delivery_signature, uuid,
    target, message) from raising an exception.

Minor comments:
In @docs/configuration.md:

  • Line 1087: The "access" heading currently uses one too many #'s (shown as
    '#### access') which breaks the TOC and markdownlint; change that heading to the
    correct level (e.g., '### access') in the env registry section so it aligns with
    surrounding headings and restores proper hierarchy—locate the heading text
    'access' and replace the four-hash form with the three-hash form to fix the
    level jump.
  • Around line 59-61: The fenced code block lacks a language tag; update the
    markdown fence containing "Python 显式 mapping / builder.override > config.toml

环境变量 > 代码默认值" to include a language (e.g., add "text" after the opening

markdown linting and tooling consistency.

In `@docs/deployment.md`:
- Around line 5-7: The blockquote in docs/deployment.md is split by an empty
line causing MD028; remove the blank line between the two blockquote lines so
the quoted content ("作为 Python 库嵌入…CLI 入口(`Undefined` /
`Undefined-webui`)行为不受库嵌入 API 影响." and "Python 版本要求:`3.11`~`3.13`(包含)。") forms a
single continuous blockquote; simply join the lines so every quoted paragraph
starts with '>' with no intervening blank line.

In `@docs/message-batching.md`:
- Around line 87-90: Update the broken relative links in
docs/message-batching.md so they point to the repository source (prefix with
../); replace "src/Undefined/services/message_batcher/" with
"../src/Undefined/services/message_batcher/",
"src/Undefined/services/ai_coordinator.py" with
"../src/Undefined/services/ai_coordinator.py" (which references functions
handle_auto_reply / handle_private_reply / _dispatch_grouped_request),
"src/Undefined/handlers/message_flow.py" with
"../src/Undefined/handlers/message_flow.py", and "src/Undefined/main.py" with
"../src/Undefined/main.py" so the links resolve correctly when rendered from
docs/.

In `@docs/python-api.md`:
- Around line 103-105: The fenced precedence line in docs/python-api.md lacks a
language tag; update the triple-backtick fence surrounding the text "Python 显式
mapping / override  >  config.toml  >  环境变量  >  代码默认值" to include a language
specifier (e.g., change ``` to ```text) so the code block is properly tagged for
markdown linting and rendering.

---

Nitpick comments:
In `@docs/configuration.md`:
- Line 185: Section numbering is out of sync: update the misplaced header "###
4.1 `[core]` 机器人核心行为" (currently under "## 5") and any other headers like those
at the noted locations (e.g., lines referenced 1012 and 1065) so that subsection
numbers follow the parent section number (renumber "### 4.1" to "### 5.1" and
continue the same renumbering pattern for all subsequent subsections and related
anchors/cross-references); ensure any internal links or table of contents
entries that reference the old numbers are updated to the new numbers to keep
anchors consistent.

In `@src/Undefined/ai/multimodal/analyzer.py`:
- Around line 19-20: The analyzer currently relies on Undefined.ai.multimodal
(referenced as _multimodal_pkg) to re-export internal constants like
_MEDIA_URL_CACHE_DIR, _MEDIA_URL_CACHE_TTL_SECONDS, _MEDIA_URL_CACHE_MAX_FILES,
_MEDIA_URL_CACHE_CLEANUP_INTERVAL_SECONDS, _MEDIA_URL_DOWNLOAD_TIMEOUT_SECONDS
and _MEDIA_URL_DOWNLOAD_TMP_SUFFIX before loading MultimodalAnalyzer; to remove
this indirection and avoid fragile aliasing, import those constants directly
from Undefined.ai.multimodal.constants (or replace uses of
_multimodal_pkg._MEDIA_URL_* with direct references to the constants module) in
analyzer.py so MultimodalAnalyzer reads the values straight from the source
(keep references to _multimodal_pkg and MultimodalAnalyzer only where needed).

In `@src/Undefined/config/parsers/helpers.py`:
- Around line 24-31: The _merge_admins function currently uses set() to
deduplicate admin_qqs + local_admins which can reorder IDs; replace the
set-based dedupe with an order-preserving approach (e.g., iterate admin_qqs then
local_admins and build a new list using a seen set or dict.fromkeys on the
concatenated sequence) so that all_admins preserves original order while
removing duplicates, and still ensure superadmin_qq is appended if present and
missing.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ef3d8fbd-9e4c-4554-89f9-1941aee9f747

📥 Commits

Reviewing files that changed from the base of the PR and between 1859133 and e73ad74.

📒 Files selected for processing (150)
  • .coderabbit.yaml
  • ARCHITECTURE.md
  • README.md
  • docs/configuration.md
  • docs/deployment.md
  • docs/development.md
  • docs/message-batching.md
  • docs/python-api.md
  • pyproject.toml
  • res/IMPORTANT/each.md
  • res/prompts/undefined.xml
  • res/prompts/undefined_nagaagent.xml
  • src/Undefined/__init__.py
  • src/Undefined/ai/client.py
  • src/Undefined/ai/client/__init__.py
  • src/Undefined/ai/client/ask_loop.py
  • src/Undefined/ai/client/queue.py
  • src/Undefined/ai/client/setup.py
  • src/Undefined/ai/llm.py
  • src/Undefined/ai/llm/__init__.py
  • src/Undefined/ai/llm/requester.py
  • src/Undefined/ai/llm/sanitize.py
  • src/Undefined/ai/llm/streaming.py
  • src/Undefined/ai/llm/thinking.py
  • src/Undefined/ai/llm/types.py
  • src/Undefined/ai/model_selector.py
  • src/Undefined/ai/multimodal/__init__.py
  • src/Undefined/ai/multimodal/analyzer.py
  • src/Undefined/ai/multimodal/constants.py
  • src/Undefined/ai/multimodal/detection.py
  • src/Undefined/ai/multimodal/parsing.py
  • src/Undefined/ai/parsing.py
  • src/Undefined/ai/prompts/__init__.py
  • src/Undefined/ai/prompts/builder.py
  • src/Undefined/ai/prompts/cognitive.py
  • src/Undefined/ai/prompts/constants.py
  • src/Undefined/ai/prompts/system_context.py
  • src/Undefined/ai/tooling.py
  • src/Undefined/ai/transports/openai_transport.py
  • src/Undefined/api/routes/memes.py
  • src/Undefined/api/routes/naga/__init__.py
  • src/Undefined/api/routes/naga/auth.py
  • src/Undefined/api/routes/naga/bind.py
  • src/Undefined/api/routes/naga/send.py
  • src/Undefined/api/routes/naga/unbind.py
  • src/Undefined/attachments/__init__.py
  • src/Undefined/attachments/models.py
  • src/Undefined/attachments/registry.py
  • src/Undefined/attachments/render.py
  • src/Undefined/attachments/segments.py
  • src/Undefined/bilibili/wbi.py
  • src/Undefined/cognitive/historian/__init__.py
  • src/Undefined/cognitive/historian/helpers.py
  • src/Undefined/cognitive/historian/tools.py
  • src/Undefined/cognitive/historian/worker.py
  • src/Undefined/cognitive/job_queue.py
  • src/Undefined/cognitive/profile_storage.py
  • src/Undefined/cognitive/service/__init__.py
  • src/Undefined/cognitive/service/helpers.py
  • src/Undefined/cognitive/service/service.py
  • src/Undefined/cognitive/vector_store.py
  • src/Undefined/config/__init__.py
  • src/Undefined/config/build_config.py
  • src/Undefined/config/config_class.py
  • src/Undefined/config/domain_parsers.py
  • src/Undefined/config/env_registry.py
  • src/Undefined/config/load_sections/__init__.py
  • src/Undefined/config/load_sections/access.py
  • src/Undefined/config/load_sections/core.py
  • src/Undefined/config/load_sections/domains.py
  • src/Undefined/config/load_sections/finalize.py
  • src/Undefined/config/load_sections/history_skills.py
  • src/Undefined/config/load_sections/integrations.py
  • src/Undefined/config/load_sections/knowledge.py
  • src/Undefined/config/load_sections/logging_tools.py
  • src/Undefined/config/load_sections/models.py
  • src/Undefined/config/load_sections/network.py
  • src/Undefined/config/loader.py
  • src/Undefined/config/manager.py
  • src/Undefined/config/parsers/__init__.py
  • src/Undefined/config/parsers/agent.py
  • src/Undefined/config/parsers/chat.py
  • src/Undefined/config/parsers/embedding.py
  • src/Undefined/config/parsers/grok.py
  • src/Undefined/config/parsers/helpers.py
  • src/Undefined/config/parsers/historian.py
  • src/Undefined/config/parsers/image.py
  • src/Undefined/config/parsers/naga.py
  • src/Undefined/config/parsers/pool.py
  • src/Undefined/config/parsers/security.py
  • src/Undefined/config/parsers/summary.py
  • src/Undefined/config/parsers/vision.py
  • src/Undefined/config/toml_io.py
  • src/Undefined/handlers.py
  • src/Undefined/handlers/__init__.py
  • src/Undefined/handlers/auto_extract.py
  • src/Undefined/handlers/message_flow.py
  • src/Undefined/handlers/poke.py
  • src/Undefined/handlers/repeat.py
  • src/Undefined/memes/_image_utils.py
  • src/Undefined/memes/ingest.py
  • src/Undefined/memes/models.py
  • src/Undefined/memes/search.py
  • src/Undefined/memes/service.py
  • src/Undefined/onebot/__init__.py
  • src/Undefined/onebot/client.py
  • src/Undefined/onebot/message.py
  • src/Undefined/py.typed
  • src/Undefined/services/ai_coordinator.py
  • src/Undefined/services/commands/bugfix.py
  • src/Undefined/services/commands/stats.py
  • src/Undefined/services/coordinator/__init__.py
  • src/Undefined/services/coordinator/background.py
  • src/Undefined/services/coordinator/batching.py
  • src/Undefined/services/coordinator/group.py
  • src/Undefined/services/coordinator/private.py
  • src/Undefined/services/message_batcher/__init__.py
  • src/Undefined/services/message_batcher/scheduler.py
  • src/Undefined/services/message_batcher/state.py
  • src/Undefined/skills/agents/code_delivery_agent/tools/read/handler.py
  • src/Undefined/skills/agents/runner.py
  • src/Undefined/skills/agents/runner/__init__.py
  • src/Undefined/skills/agents/runner/context.py
  • src/Undefined/skills/agents/runner/loop.py
  • src/Undefined/skills/agents/runner/tools.py
  • src/Undefined/skills/tools/__init__.py
  • src/Undefined/skills/tools/bilibili_video/handler.py
  • src/Undefined/skills/tools/fetch_image_uid/handler.py
  • src/Undefined/skills/tools/get_current_time/handler.py
  • src/Undefined/skills/tools/get_picture/handler.py
  • src/Undefined/skills/tools/get_user_info/handler.py
  • src/Undefined/skills/tools/python_interpreter/handler.py
  • src/Undefined/skills/tools/qq_like/handler.py
  • src/Undefined/skills/tools/task_progress/handler.py
  • src/Undefined/utils/paths.py
  • src/Undefined/utils/render_cache.py
  • src/Undefined/utils/resources.py
  • src/Undefined/utils/sender_helpers.py
  • src/Undefined/webui/routes/_runtime.py
  • tests/test_ai_client_setup_paths.py
  • tests/test_ai_coordinator_queue_routing.py
  • tests/test_cli_startup_compat.py
  • tests/test_config_env_only.py
  • tests/test_config_env_registry.py
  • tests/test_config_from_mapping.py
  • tests/test_end_defer_co_call.py
  • tests/test_handlers_meme_annotation.py
  • tests/test_llm_retry_suppression.py
  • tests/test_package_layout.py
  • tests/test_public_api_imports.py
💤 Files with no reviewable changes (9)
  • src/Undefined/ai/parsing.py
  • src/Undefined/config/domain_parsers.py
  • src/Undefined/ai/model_selector.py
  • src/Undefined/cognitive/job_queue.py
  • src/Undefined/ai/llm.py
  • src/Undefined/ai/transports/openai_transport.py
  • src/Undefined/cognitive/vector_store.py
  • src/Undefined/ai/client.py
  • src/Undefined/cognitive/profile_storage.py

Comment thread src/Undefined/ai/client/ask_loop.py Outdated
Comment thread src/Undefined/ai/client/queue.py
Comment thread src/Undefined/ai/client/setup.py
Comment thread src/Undefined/ai/client/setup.py
Comment thread src/Undefined/ai/llm/requester.py
Comment thread src/Undefined/config/load_sections/finalize.py Outdated
Comment thread src/Undefined/config/load_sections/history_skills.py
Comment thread src/Undefined/config/load_sections/integrations.py
Comment thread src/Undefined/config/load_sections/knowledge.py
Comment thread src/Undefined/config/load_sections/logging_tools.py
Harden queued LLM retries, pending-call cleanup, tool filtering, config
bounds, attachment rendering, and privacy-safe prompt cache keys.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Warning

Docstrings generation - SKIPPED

Too many files!

This PR contains 151 files, which is 1 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

✅ Created PR with unit tests: #62

69gg and others added 2 commits May 24, 2026 10:31

@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.

🧹 Nitpick comments (1)
src/Undefined/__init__.py (1)

52-59: ⚡ Quick win

Keep the lazy root exports statically typed.

Lines 52-59 route every package-level export through __getattr__(...) -> Any, so external from Undefined import Config / AIClient users can lose concrete types even though the wheel now ships py.typed. Please verify this surface with mypy/pyright and add TYPE_CHECKING imports or a small __init__.pyi if needed.

Example pattern
-from typing import Any
+from typing import Any, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from Undefined.config import Config, get_config, set_config
+    from Undefined.ai import AIClient
+    # ...repeat for the remaining public re-exports
🤖 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 `@src/Undefined/__init__.py` around lines 52 - 59, The current __getattr__
fallback returns Any for lazy exports (defined in _LAZY_IMPORTS) which loses
static types for consumers (e.g., Config, AIClient); fix by adding explicit
TYPE_CHECKING-only imports in this module that import and re-export the real
types (use "if TYPE_CHECKING: from .config import Config; from .client import
AIClient") so mypy/pyright see concrete types, or alternatively add a small
__init__.pyi stub that declares the exported names and their types; keep the
runtime lazy behavior via the existing __getattr__ and _LAZY_IMPORTS while
ensuring the type checker sees the real symbols.
🤖 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.

Nitpick comments:
In `@src/Undefined/__init__.py`:
- Around line 52-59: The current __getattr__ fallback returns Any for lazy
exports (defined in _LAZY_IMPORTS) which loses static types for consumers (e.g.,
Config, AIClient); fix by adding explicit TYPE_CHECKING-only imports in this
module that import and re-export the real types (use "if TYPE_CHECKING: from
.config import Config; from .client import AIClient") so mypy/pyright see
concrete types, or alternatively add a small __init__.pyi stub that declares the
exported names and their types; keep the runtime lazy behavior via the existing
__getattr__ and _LAZY_IMPORTS while ensuring the type checker sees the real
symbols.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f0507d0-7d57-4561-931b-3a7080ddee82

📥 Commits

Reviewing files that changed from the base of the PR and between 531b6a6 and b46009f.

⛔ Files ignored due to path filters (3)
  • apps/undefined-console/package-lock.json is excluded by !**/package-lock.json
  • apps/undefined-console/src-tauri/Cargo.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • CHANGELOG.md
  • apps/undefined-console/package.json
  • apps/undefined-console/src-tauri/Cargo.toml
  • apps/undefined-console/src-tauri/tauri.conf.json
  • pyproject.toml
  • src/Undefined/__init__.py
✅ Files skipped from review due to trivial changes (4)
  • apps/undefined-console/src-tauri/tauri.conf.json
  • apps/undefined-console/src-tauri/Cargo.toml
  • apps/undefined-console/package.json
  • CHANGELOG.md

Co-authored-by: Cursor <cursoragent@cursor.com>

@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.

🧹 Nitpick comments (1)
src/Undefined/__init__.py (1)

25-64: ⚡ Quick win

Derive __all__ from _LAZY_IMPORTS to avoid export drift.

__all__ and _LAZY_IMPORTS currently duplicate the same symbol list, which can diverge on future edits.

♻️ Proposed refactor
-__all__ = [
-    "__version__",
-    "Config",
-    "get_config",
-    "set_config",
-    "AIClient",
-    "ToolRegistry",
-    "AgentRegistry",
-    "PipelineRegistry",
-    "BaseRegistry",
-    "AnthropicSkillRegistry",
-    "CognitiveService",
-    "KnowledgeManager",
-    "MemeService",
-    "AttachmentRegistry",
-    "RuntimeAPIServer",
-    "RuntimeAPIContext",
-]
-
-# symbol -> (module_path, attribute_name);首次访问时才 importlib 加载
 _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
     "Config": ("Undefined.config", "Config"),
     "get_config": ("Undefined.config", "get_config"),
@@
     "RuntimeAPIServer": ("Undefined.api.app", "RuntimeAPIServer"),
     "RuntimeAPIContext": ("Undefined.api._context", "RuntimeAPIContext"),
 }
+
+__all__ = ["__version__", *_LAZY_IMPORTS.keys()]
🤖 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 `@src/Undefined/__init__.py` around lines 25 - 64, Replace the hard-coded
__all__ with a derived list built from the keys of _LAZY_IMPORTS (keeping
"__version__" as an explicit export) to avoid drift; update the module so
"__all__" is assigned from ["__version__"] plus list(_LAZY_IMPORTS.keys()) (or
equivalent that preserves insertion order) and ensure the resulting object
remains a list of strings; touch the __all__ and _LAZY_IMPORTS symbols in
src/Undefined/__init__.py.
🤖 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.

Nitpick comments:
In `@src/Undefined/__init__.py`:
- Around line 25-64: Replace the hard-coded __all__ with a derived list built
from the keys of _LAZY_IMPORTS (keeping "__version__" as an explicit export) to
avoid drift; update the module so "__all__" is assigned from ["__version__"]
plus list(_LAZY_IMPORTS.keys()) (or equivalent that preserves insertion order)
and ensure the resulting object remains a list of strings; touch the __all__ and
_LAZY_IMPORTS symbols in src/Undefined/__init__.py.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5035676b-2b07-480c-a486-532bda280b49

📥 Commits

Reviewing files that changed from the base of the PR and between b46009f and c85ad66.

📒 Files selected for processing (1)
  • src/Undefined/__init__.py

Co-authored-by: Cursor <cursoragent@cursor.com>
@69gg 69gg merged commit e954e4d into main May 24, 2026
3 checks passed
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