修复 uv虚拟环境下,第一次启动报错,并且无法安装插件的问题。 - #1523
Open
436and251 wants to merge 2 commits into
Open
Conversation
Contributor
Reviewer's Guide此 PR 修复了在基于 uv 的 Python 3.12 环境中插件安装/卸载的问题,改进了系统更新安装以使用相同的包管理器抽象,加强了针对较新 MCP 版本的错误处理,生成了安全的默认 Web secret key 以保证首次登录可靠工作,并添加/调整了测试和依赖以匹配这些行为。 在插件和系统更新中使用 build_package_manager_command 的序列图sequenceDiagram
title build_package_manager_command selection for uv or pip
participant PluginLoader
participant SystemRoutes
participant build_package_manager_command
participant uv
participant pip
PluginLoader->>build_package_manager_command: build_package_manager_command("install", index_url, package_spec)
SystemRoutes->>build_package_manager_command: build_package_manager_command("install", backend_file)
alt [uv available]
build_package_manager_command->>uv: shutil.which("uv")
uv-->>build_package_manager_command: uv path
build_package_manager_command-->>PluginLoader: [uv, "pip", action, "--python", sys.executable, args]
build_package_manager_command-->>SystemRoutes: [uv, "pip", action, "--python", sys.executable, args]
else [uv not available]
build_package_manager_command->>pip: importlib.util.find_spec("pip")
pip-->>build_package_manager_command: pip module found
build_package_manager_command-->>PluginLoader: [sys.executable, "-m", "pip", action, args]
build_package_manager_command-->>SystemRoutes: [sys.executable, "-m", "pip", action, args]
end
PluginLoader->>PluginLoader: install_plugin / uninstall_plugin uses cmd
SystemRoutes->>SystemRoutes: perform_update uses cmd
首次 Web 登录流程的序列图sequenceDiagram
title First time login flow with secret_key and AuthService
actor User
participant WebAuthRoutes
participant AuthService
User->>WebAuthRoutes: POST /login
WebAuthRoutes->>AuthService: is_first_time()
alt [first time]
AuthService-->>WebAuthRoutes: True
WebAuthRoutes->>AuthService: create_access_token(timedelta_days_1)
AuthService-->>WebAuthRoutes: access_token
WebAuthRoutes->>AuthService: save_password(login_data.password)
WebAuthRoutes-->>User: TokenResponse(access_token)
else [not first time]
AuthService-->>WebAuthRoutes: False
WebAuthRoutes->>AuthService: verify_password(login_data.password)
AuthService-->>WebAuthRoutes: verification_result
opt [password valid]
WebAuthRoutes->>AuthService: create_access_token(timedelta_days_1)
AuthService-->>WebAuthRoutes: access_token
WebAuthRoutes-->>User: TokenResponse(access_token)
end
end
文件级改动
Tips and commands与 Sourcery 交互
自定义你的体验访问你的 dashboard 来:
获取帮助Original review guide in EnglishReviewer's GuideThis PR fixes plugin installation/uninstallation in uv-based Python 3.12 environments, improves system update installation to use the same package manager abstraction, hardens MCP error handling against newer mcp versions, generates a secure default web secret key to make first login work reliably, and adds/adjusts tests and dependencies to match these behaviors. Sequence diagram for build_package_manager_command usage in plugin and system updatessequenceDiagram
title build_package_manager_command selection for uv or pip
participant PluginLoader
participant SystemRoutes
participant build_package_manager_command
participant uv
participant pip
PluginLoader->>build_package_manager_command: build_package_manager_command("install", index_url, package_spec)
SystemRoutes->>build_package_manager_command: build_package_manager_command("install", backend_file)
alt [uv available]
build_package_manager_command->>uv: shutil.which("uv")
uv-->>build_package_manager_command: uv path
build_package_manager_command-->>PluginLoader: [uv, "pip", action, "--python", sys.executable, args]
build_package_manager_command-->>SystemRoutes: [uv, "pip", action, "--python", sys.executable, args]
else [uv not available]
build_package_manager_command->>pip: importlib.util.find_spec("pip")
pip-->>build_package_manager_command: pip module found
build_package_manager_command-->>PluginLoader: [sys.executable, "-m", "pip", action, args]
build_package_manager_command-->>SystemRoutes: [sys.executable, "-m", "pip", action, args]
end
PluginLoader->>PluginLoader: install_plugin / uninstall_plugin uses cmd
SystemRoutes->>SystemRoutes: perform_update uses cmd
Sequence diagram for the first-time web login flowsequenceDiagram
title First time login flow with secret_key and AuthService
actor User
participant WebAuthRoutes
participant AuthService
User->>WebAuthRoutes: POST /login
WebAuthRoutes->>AuthService: is_first_time()
alt [first time]
AuthService-->>WebAuthRoutes: True
WebAuthRoutes->>AuthService: create_access_token(timedelta_days_1)
AuthService-->>WebAuthRoutes: access_token
WebAuthRoutes->>AuthService: save_password(login_data.password)
WebAuthRoutes-->>User: TokenResponse(access_token)
else [not first time]
AuthService-->>WebAuthRoutes: False
WebAuthRoutes->>AuthService: verify_password(login_data.password)
AuthService-->>WebAuthRoutes: verification_result
opt [password valid]
WebAuthRoutes->>AuthService: create_access_token(timedelta_days_1)
AuthService-->>WebAuthRoutes: access_token
WebAuthRoutes-->>User: TokenResponse(access_token)
end
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些整体反馈:
- 在首次登录的分支中,现在在调用
save_password之前就创建了访问令牌;建议在发放令牌之前保持副作用(持久化密码哈希),这样如果保存失败,系统就不会处于“部分初始化但已有有效令牌”的状态。 - 新的
WebConfig.secret_key的default_factory会在每次进程启动时生成一个全新的随机密钥,这会在重启后使所有现有 JWT 失效;如果系统需要长期有效的令牌,你可能需要确保密钥在重启之间保持稳定(例如从配置或存储中加载)。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- 在首次登录的分支中,现在在调用 `save_password` 之前就创建了访问令牌;建议在发放令牌之前保持副作用(持久化密码哈希),这样如果保存失败,系统就不会处于“部分初始化但已有有效令牌”的状态。
- 新的 `WebConfig.secret_key` 的 `default_factory` 会在每次进程启动时生成一个全新的随机密钥,这会在重启后使所有现有 JWT 失效;如果系统需要长期有效的令牌,你可能需要确保密钥在重启之间保持稳定(例如从配置或存储中加载)。
## Individual Comments
### Comment 1
<location path="kirara_ai/plugin_manager/utils.py" line_range="8-10" />
<code_context>
from typing import Any, Dict, Optional
+def build_package_manager_command(action: str, *args: str) -> list[str]:
+ """Build a uv/pip command targeting the current Python environment."""
+ if uv := shutil.which("uv"):
+ return [uv, "pip", action, "--python", sys.executable, *args]
+ if importlib.util.find_spec("pip") is not None:
</code_context>
<issue_to_address>
**issue:** 通过 `uv` 执行卸载是交互式的,在非交互环境中可能会挂起。
在 `uv` 分支中,`build_package_manager_command` 构造的是 `uv pip uninstall`,但没有提供等价于 pip `-y` 的非交互选项。由于 `PluginLoader.uninstall_plugin` 和 Web 更新路径在没有 TTY 的环境中运行,这会导致 `uv pip uninstall` 阻塞等待确认。请添加合适的 `uv` 标志或对齐现有的 `assume_yes` 行为,以确保在两种后端中卸载流程都保持非交互式。
</issue_to_address>
### Comment 2
<location path="kirara_ai/config/global_config.py" line_range="112-113" />
<code_context>
host: str = Field(default="127.0.0.1", description="Web服务绑定的IP地址")
port: int = Field(default=8080, description="Web服务端口号")
- secret_key: str = Field(default="", description="Web服务的密钥,用于JWT等加密")
+ secret_key: str = Field(
+ default_factory=lambda: secrets.token_hex(32),
+ description="Web服务的密钥,用于JWT等加密",
+ )
</code_context>
<issue_to_address>
**🚨 suggestion (security):** 如果不进行持久化,自动生成 `secret_key` 会在每次重启时使现有令牌失效。
由于 `default_factory` 会在未显式提供值时,每次实例化配置都生成新的 `secret_key`,当密钥没有被持久化时,所有现有的 JWT/会话将在每次重启后失效。如果你只希望在初始设置时进行一次密钥轮换,需要确保生成的密钥被存储并复用(例如持久化到磁盘),或者在配置中强制要求显式提供 `secret_key`,而不是在没有值时静默生成新密钥。
Suggested implementation:
```python
import secrets
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
class WebConfig(BaseModel):
host: str = Field(default="127.0.0.1", description="Web服务绑定的IP地址")
port: int = Field(default=8080, description="Web服务端口号")
secret_key: str = Field(
...,
description="Web服务的密钥,用于JWT等加密(必须显式配置,避免重启时密钥变化导致JWT/会话失效)",
)
password_file: str = Field(
default="./data/web/password.hash", description="密码哈希存储路径"
)
```
1. 如果 `secrets` 没有在 `global_config.py` 的其他位置使用,可以安全地移除 `import secrets` 行以避免未使用的导入。
2. 确保在所有实例化 `WebConfig` 的地方(例如从配置文件或环境变量加载)都显式提供 `secret_key`;否则由于它现在是必填字段,pydantic 将抛出校验错误。
3. 如果之后决定支持“一次自动生成并持久化”的方案,需要在该模型之外添加额外逻辑,仅在首次生成密钥时存储它(例如写入配置文件或密钥库),并在之后将存储的值传入 `WebConfig`。帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进后续评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- In the first-time login branch, the access token is now created before
save_passwordis called; consider keeping the side-effect (persisting the password hash) before issuing a token so a failure to save cannot leave the system in a partially-initialized state with a valid token. - The new
WebConfig.secret_keydefault_factory generates a fresh random key on each process start, which will invalidate all existing JWTs after restart; if long-lived tokens are expected, you may want to ensure the secret is stable across restarts (e.g., loaded from configuration or storage).
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the first-time login branch, the access token is now created before `save_password` is called; consider keeping the side-effect (persisting the password hash) before issuing a token so a failure to save cannot leave the system in a partially-initialized state with a valid token.
- The new `WebConfig.secret_key` default_factory generates a fresh random key on each process start, which will invalidate all existing JWTs after restart; if long-lived tokens are expected, you may want to ensure the secret is stable across restarts (e.g., loaded from configuration or storage).
## Individual Comments
### Comment 1
<location path="kirara_ai/plugin_manager/utils.py" line_range="8-10" />
<code_context>
from typing import Any, Dict, Optional
+def build_package_manager_command(action: str, *args: str) -> list[str]:
+ """Build a uv/pip command targeting the current Python environment."""
+ if uv := shutil.which("uv"):
+ return [uv, "pip", action, "--python", sys.executable, *args]
+ if importlib.util.find_spec("pip") is not None:
</code_context>
<issue_to_address>
**issue:** Uninstall via `uv` will be interactive and can hang in non-interactive contexts.
In the `uv` branch, `build_package_manager_command` builds `uv pip uninstall` without a non-interactive equivalent to pip’s `-y`. Since `PluginLoader.uninstall_plugin` and the web update path run this without a TTY, `uv pip uninstall` can block waiting for confirmation. Please add the appropriate `uv` flag or mirror the existing `assume_yes` behaviour so uninstall stays non-interactive for both backends.
</issue_to_address>
### Comment 2
<location path="kirara_ai/config/global_config.py" line_range="112-113" />
<code_context>
host: str = Field(default="127.0.0.1", description="Web服务绑定的IP地址")
port: int = Field(default=8080, description="Web服务端口号")
- secret_key: str = Field(default="", description="Web服务的密钥,用于JWT等加密")
+ secret_key: str = Field(
+ default_factory=lambda: secrets.token_hex(32),
+ description="Web服务的密钥,用于JWT等加密",
+ )
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Auto-generating `secret_key` can invalidate existing tokens on each restart if not persisted.
Because `default_factory` generates a new `secret_key` whenever the config is instantiated without an explicit value, all existing JWTs/sessions will be invalidated on each restart when the key isn’t persisted. If you only want key rotation on initial setup, ensure the generated key is stored and reused (e.g., persisted to disk), or require an explicit `secret_key` in configuration instead of silently generating a new one.
Suggested implementation:
```python
import secrets
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
class WebConfig(BaseModel):
host: str = Field(default="127.0.0.1", description="Web服务绑定的IP地址")
port: int = Field(default=8080, description="Web服务端口号")
secret_key: str = Field(
...,
description="Web服务的密钥,用于JWT等加密(必须显式配置,避免重启时密钥变化导致JWT/会话失效)",
)
password_file: str = Field(
default="./data/web/password.hash", description="密码哈希存储路径"
)
```
1. If `secrets` is not used elsewhere in `global_config.py`, you can safely remove the `import secrets` line to avoid an unused import.
2. Ensure that wherever `WebConfig` is instantiated (e.g., loading from a config file or environment variables), `secret_key` is always provided; otherwise pydantic will raise a validation error because it is now required.
3. If you later decide to support one-time auto-generation with persistence, you'll need additional logic outside this model to generate the key once, store it (e.g., in a config file or keystore), and pass the stored value into `WebConfig`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
…JWT 失效。 现在改为: WebConfig.secret_key 默认保持为空。 应用启动时仅在密钥为空时生成 64 字符随机密钥。 立即通过现有 ConfigLoader 写入 data/config.yaml。 后续启动直接复用已有密钥,不再轮换。 已存在的非空密钥不会被覆盖。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
clone之后,使用python3.12的新uv虚拟环境运行的项目。
Summary by Sourcery
在基于 uv 的环境中改进插件管理和系统更新的兼容性,并更新 MCP 集成以适配更新的库版本。
新功能:
错误修复:
增强:
构建:
测试:
Original summary in English
Summary by Sourcery
Improve plugin management and system update compatibility with uv-based environments and update MCP integration for newer library versions.
New Features:
Bug Fixes:
Enhancements:
Build:
Tests: