Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,47 @@ jobs:
- name: Install Python dependencies
run: uv sync --group ci -p ${{ env.PYTHON_VERSION }}

- name: Validate release tag matches package version
env:
TAG_NAME: ${{ github.ref_name }}
run: |
uv run python - <<'PY'
import os
import pathlib
import re
import sys
import tomllib

tag_name = os.environ["TAG_NAME"]
expected_version = tag_name.removeprefix("v")

pyproject = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8"))
project_version = str(pyproject["project"]["version"]).strip()

init_text = pathlib.Path("src/Undefined/__init__.py").read_text(encoding="utf-8")
match = re.search(r'__version__\s*=\s*"([^"]+)"', init_text)
if match is None:
raise SystemExit("Could not find __version__ in src/Undefined/__init__.py")
init_version = match.group(1).strip()

errors: list[str] = []
if project_version != init_version:
errors.append(
f"Version mismatch: pyproject.toml={project_version}, src/Undefined/__init__.py={init_version}"
)
if project_version != expected_version:
errors.append(
f"Tag/version mismatch: tag={tag_name}, expected package version={expected_version}, actual={project_version}"
)

if errors:
raise SystemExit("\n".join(errors))

print(
f"Validated release version {project_version} from tag {tag_name}, pyproject.toml, and src/Undefined/__init__.py"
)
PY

- name: Cache Ruff
uses: actions/cache@v4
with:
Expand Down Expand Up @@ -466,5 +507,31 @@ jobs:
name: python-dist
path: dist

- name: Verify downloaded distributions
env:
TAG_NAME: ${{ github.ref_name }}
run: |
uv run python - <<'PY'
import os
import pathlib

expected_version = os.environ["TAG_NAME"].removeprefix("v")
dist_dir = pathlib.Path("dist")
files = sorted(path.name for path in dist_dir.glob("*"))
if not files:
raise SystemExit("No Python distributions were downloaded")

bad = [name for name in files if f"-{expected_version}" not in name]
if bad:
raise SystemExit(
"Downloaded distributions do not match release tag version "
f"{expected_version}: {bad}"
)

print("Distributions ready for publish:")
for name in files:
print(f" - {name}")
PY

- name: Publish to PyPI
run: uv publish dist/*
run: uv publish --check-url https://pypi.org/simple/ dist/*
4 changes: 2 additions & 2 deletions apps/undefined-console/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/undefined-console/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "undefined-console",
"private": true,
"version": "3.2.3",
"version": "3.2.4",
"type": "module",
"scripts": {
"tauri": "tauri",
Expand Down
2 changes: 1 addition & 1 deletion apps/undefined-console/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/undefined-console/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "undefined_console"
version = "3.2.3"
version = "3.2.4"
description = "Undefined cross-platform management console"
authors = ["Undefined contributors"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion apps/undefined-console/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Undefined Console",
"version": "3.2.3",
"version": "3.2.4",
"identifier": "com.undefined.console",
"build": {
"beforeDevCommand": "npm run dev",
Expand Down
63 changes: 59 additions & 4 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,61 @@ responses_force_stateless_replay = false
# en: Extra request-body params (optional), e.g. temperature or vendor-specific fields.
[models.security.request_params]

# zh: Naga 外发消息审核模型配置(仅用于 Naga 通过 Runtime API 发消息前的内容审核)。
# zh: 若无必要可不填写;未配置时回退到 [models.security]。
# en: Naga outbound moderation model config (used only before Naga sends messages through the Runtime API).
# en: Optional; falls back to [models.security] when omitted.
[models.naga]
# zh: OpenAI-compatible 基址 URL,例如 https://api.openai.com/v1(legacy "/chat/completions" 已弃用但仍兼容)。
# en: OpenAI-compatible base URL, e.g. https://api.openai.com/v1. Note: legacy "/chat/completions" is deprecated but still supported.
api_url = ""
# zh: Naga 审核模型 API Key。
# en: Naga moderation model API key.
api_key = ""
# zh: Naga 审核模型名称。
# en: Naga moderation model name.
model_name = ""
# zh: 可选限制:最大生成 tokens。
# en: Optional limit: max generation tokens.
max_tokens = 160
# zh: 队列发车间隔(秒)。
# en: Queue interval (seconds).
queue_interval_seconds = 1.0
# zh: API 模式:传统 chat.completions 或新版 responses。
# en: API mode: classic chat.completions or the newer responses API.
api_mode = "chat_completions"
# zh: 是否启用 reasoning.effort。
# en: Enable reasoning.effort.
reasoning_enabled = false
# zh: reasoning effort 档位。
# en: reasoning effort level.
reasoning_effort = "medium"
# zh: 是否启用 thinking(思维链)。
# en: Enable thinking (reasoning).
thinking_enabled = false
# zh: thinking 预算 tokens。
# en: Thinking-budget tokens.
thinking_budget_tokens = 0
# zh: 是否在请求中发送 budget_tokens(关闭后由提供商决定思维预算)。
# en: Whether to include budget_tokens in the request (if disabled, the provider decides the thinking budget).
thinking_include_budget = true
# zh: reasoning effort 传参风格:openai(reasoning.effort)/ anthropic(output_config.effort)。
# en: Reasoning effort wire format: openai (reasoning.effort) / anthropic (output_config.effort).
reasoning_effort_style = "openai"
# zh: 思维链工具调用兼容:启用后在多轮工具调用中回传 reasoning_content,避免部分模型返回 400。
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = true
# zh: Responses API 的 tool_choice 兼容模式。
# en: Responses API tool_choice compatibility mode.
responses_tool_choice_compat = false
# zh: Responses API 续轮强制降级。
# en: Responses API force stateless replay.
responses_force_stateless_replay = false

# zh: 额外请求体参数(可选),可用于 temperature 或供应商私有参数。
# en: Extra request-body params (optional), e.g. temperature or vendor-specific fields.
[models.naga.request_params]

# zh: Agent 模型配置(用于执行 agents)。
# en: Agent model config (used to run agents).
[models.agent]
Expand Down Expand Up @@ -886,9 +941,9 @@ job_max_retries = 3
# en: To get NagaAgent answering without external callbacks, only enable nagaagent_mode_enabled.
# en: ⚠️ Advanced feature for NagaAgent integration. Not recommended for regular users.
[naga]
# zh: 是否启用外部网关集成(回调 API、/naga 命令、绑定管理)。
# zh: 是否启用外部网关集成(/naga bind/unbind、远端绑定回调、消息发送、解绑 API)。
# zh: 需同时开启 [features].nagaagent_mode_enabled 才生效。
# en: Enable external gateway integration (callback API, /naga command, bindings).
# en: Enable external gateway integration (/naga bind/unbind, bind callback, message send, unbind API).
# en: Requires [features].nagaagent_mode_enabled = true to take effect.
enabled = false
# zh: Naga 服务器 API 地址。
Expand All @@ -897,6 +952,6 @@ api_url = ""
# zh: 双方共享密钥(Undefined ↔ Naga 身份验证)。
# en: Shared secret key for authentication between Undefined and Naga.
api_key = ""
# zh: Naga 服务群聊名单:绑定/回调群发仅限这些群
# en: Allowed groups for Naga binding and group callback delivery.
# zh: Naga 白名单群:/naga 在这些群中才可见;绑定和群聊投递也仅限这些群
# en: Naga allowlisted groups: /naga is only visible here, and binding/group delivery are restricted to these groups.
allowed_groups = []
46 changes: 33 additions & 13 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ model_name = "gpt-4o-mini"

| 字段 | 默认值 | 说明 | 约束/回退 |
|---|---:|---|---|
| `ws_url` | `"ws://127.0.0.1:3001"` | OneBot WebSocket 地址 | 严格模式必填 |
| `ws_url` | `""` | OneBot WebSocket 地址 | 模板示例通常写 `ws://127.0.0.1:3001`;严格模式必填 |
| `token` | `""` | OneBot token | 同时用于 URL 参数与 `Authorization` 头 |

`onebot.*` 变更需要重启进程才能生效。
Expand Down Expand Up @@ -215,7 +215,28 @@ model_name = "gpt-4o-mini"
- 若 `api_url/api_key/model_name` 任一缺失,会自动回退为 chat 模型(并告警)。
- 回退时会继承 chat 的 `api_mode`、`reasoning_*`、`responses_tool_choice_compat`、`responses_force_stateless_replay` 与 `request_params`;旧 `thinking_*` 仍保持安全模型自身默认值。

### 4.4.5 `[models.agent]` Agent 执行模型
### 4.4.5 `[models.naga]` Naga 审核模型

用途:
- 仅用于 `POST /api/v1/naga/messages/send` 前的消息审核。

默认:
- `max_tokens=160`
- `queue_interval_seconds=1.0`
- `api_mode="chat_completions"`
- `reasoning_enabled=false`
- `reasoning_effort="medium"`
- `thinking_enabled=false`
- `thinking_budget_tokens=0`
- `thinking_include_budget=true`
- `thinking_tool_call_compat=true`
- `responses_tool_choice_compat=false`
- `responses_force_stateless_replay=false`

关键回退逻辑:
- 若整个节缺失或 `api_url/api_key/model_name` 任一缺失:完整回退到 `models.security`,并沿用安全模型的请求参数。

### 4.4.6 `[models.agent]` Agent 执行模型

默认:
- `max_tokens=4096`
Expand All @@ -227,14 +248,14 @@ model_name = "gpt-4o-mini"
- `responses_tool_choice_compat=false`
- `responses_force_stateless_replay=false`

### 4.4.6 `[models.historian]` 史官模型
### 4.4.7 `[models.historian]` 史官模型

- 用于认知记忆后台改写。
- 若整个节缺失或为空:完整回退到 `models.agent`。
- 若部分字段缺失:逐项继承 agent 配置,包括 `api_mode`、`reasoning_*`、`thinking_*`、`responses_tool_choice_compat`、`responses_force_stateless_replay` 与 `request_params`。
- `queue_interval_seconds<=0` 时回退到 agent 的间隔。

### 4.4.7 模型池
### 4.4.8 模型池

相关节:
- `[models.chat.pool]`
Expand Down Expand Up @@ -266,7 +287,7 @@ model_name = "gpt-4o-mini"
2. 对应池 `enabled=true`
3. 池列表非空

### 4.4.8 `[models.embedding]` 嵌入模型
### 4.4.9 `[models.embedding]` 嵌入模型

| 字段 | 默认值 | 说明 |
|---|---:|---|
Expand All @@ -279,7 +300,7 @@ model_name = "gpt-4o-mini"
| `document_instruction` | `""` | 文档前缀 |
| `request_params` | `{}` | 额外请求体参数;保留字段如 `model`/`input`/`dimensions` 会忽略 |

### 4.4.9 `[models.rerank]` 重排模型
### 4.4.10 `[models.rerank]` 重排模型

| 字段 | 默认值 | 说明 |
|---|---:|---|
Expand Down Expand Up @@ -578,8 +599,6 @@ model_name = "gpt-4o-mini"
| `enabled` | `true` | 开启认知记忆 |
| `bot_name` | `Undefined` | 史官改写中使用的 bot 名称 |

说明:当前版本解析器尚未从 `config.toml` 显式读取 `cognitive.bot_name`,运行时会保持默认值 `Undefined`。

### 4.24.2 `[cognitive.vector_store]`

| 字段 | 默认值 | 说明 |
Expand Down Expand Up @@ -639,7 +658,7 @@ model_name = "gpt-4o-mini"

> **⚠️ 此功能面向与 NagaAgent 对接的高级场景,普通用户不建议开启。**

启用后允许 NagaAgent 通过绑定审批机制向 QQ 群/用户发送回调消息。鉴权采用双层模型:共享密钥 `api_key` 验证服务器身份 + 每个绑定独立的 scoped token 验证调用权限
启用后允许 NagaAgent 通过绑定审批机制向 QQ 群/用户发送回调消息。共享密钥统一使用 `Authorization: Bearer {naga.api_key}`,其中 `messages/send` 与 `unbind` 还会额外校验 `bind_uuid + naga_id + delivery_signature`

**开关分层**:

Expand All @@ -648,14 +667,14 @@ model_name = "gpt-4o-mini"
| `[features].nagaagent_mode_enabled` | 总开关:AI 侧行为(提示词切换、工具暴露) | `false` |
| `[naga].enabled` | 子开关:外部网关集成(回调 API、`/naga` 命令、绑定管理) | `false` |

- 仅当两者均为 `true` 时,外部网关集成生效(API 端点注册、`/naga` 命令可用)
- 仅当 `[api].enabled = true`、`[features].nagaagent_mode_enabled = true`、`[naga].enabled = true` 三者同时成立时,外部网关集成才会生效(API 端点注册、`/naga` 命令可用)
- 若只需 NagaAgent 解答能力而不需要外部回调联动,可只开启 `nagaagent_mode_enabled`
- `nagaagent_mode_enabled = false` 时强制关闭所有 Naga 功能,无论 `naga.enabled` 值

| 字段 | 默认值 | 说明 | 约束/回退 |
|---|---:|---|---|
| `enabled` | `false` | 是否启用外部网关集成 | 需同时开启 `nagaagent_mode_enabled` |
| `api_url` | `""` | Naga 服务器 API 地址 | 为空时 token 同步/删除操作跳过 |
| `api_url` | `""` | Naga 服务器 API 地址 | 为空时无法向远端提交 bind request / revoke 同步 |
| `api_key` | `""` | Undefined ↔ Naga 共享密钥 | 回调端点通过 `Authorization: Bearer` 校验 |
| `allowed_groups` | `[]` | Naga 服务群聊名单 | 绑定命令和回调群发仅限名单内的群 |

Expand All @@ -664,7 +683,8 @@ model_name = "gpt-4o-mini"
- 私聊场景不受 `allowed_groups` 限制
- 回调群发仅发到绑定时的群(该群须仍在 `allowed_groups` 内)
- 回调私聊只需开关开启,不受 `allowed_groups` 限制
- `/api/v1/naga/*` 端点仅在两个开关均开启时注册
- `/api/v1/naga/*` 端点仅在 `api.enabled`、`nagaagent_mode_enabled`、`naga.enabled` 三者均开启时注册
- Runtime API 关闭时,`/naga` 命令也不会在 `/help` 中显示

**数据存储**:绑定数据持久化在 `data/naga_bindings.json`,Unix 下自动 `chmod 600`。

Expand Down Expand Up @@ -724,7 +744,7 @@ model_name = "gpt-4o-mini"
- `ONEBOT_WS_URL` / `ONEBOT_TOKEN`
- `CHAT_MODEL_API_URL` / `CHAT_MODEL_API_KEY` / `CHAT_MODEL_NAME`
- `CHAT_MODEL_API_MODE` / `CHAT_MODEL_REASONING_ENABLED` / `CHAT_MODEL_REASONING_EFFORT` / `CHAT_MODEL_RESPONSES_TOOL_CHOICE_COMPAT` / `CHAT_MODEL_RESPONSES_FORCE_STATELESS_REPLAY`
- `VISION_MODEL_*` / `AGENT_MODEL_*` / `SECURITY_MODEL_*` / `HISTORIAN_MODEL_*`
- `VISION_MODEL_*` / `AGENT_MODEL_*` / `SECURITY_MODEL_*` / `NAGA_MODEL_*` / `HISTORIAN_MODEL_*`
- 上述模型环境变量同样覆盖 `*_THINKING_ENABLED`、`*_THINKING_BUDGET_TOKENS`、`*_THINKING_TOOL_CALL_COMPAT`、`*_RESPONSES_TOOL_CHOICE_COMPAT`、`*_RESPONSES_FORCE_STATELESS_REPLAY`
- `EMBEDDING_MODEL_*` / `RERANK_MODEL_*`
- `SEARXNG_URL`
Expand Down
Loading
Loading