Commit Graph
28 Commits
Author SHA1 Message Date
qing d29bb5504a feat(aibox): L-batch profile polish (display name, prefix escape, temperature, reasoning aliases)
L1 display name: client.name now resolves to the profile's
display_name field, falling back to the profile key — the dialogue
context no longer labels Kimi/Qwen sessions "DeepSeek". The legacy
flat api section keeps "DeepSeek" (zero breakage).
_resolve_model_profile now returns (profile, model, profile_name).

L2 prefix escape: "model:NAME" forces bare-model semantics even when
a profile happens to be named NAME; "profile:NAME" forces profile
semantics and raises a clear error (listing available profiles) when
missing, instead of silently falling back to bare-name behavior.
Empty prefixes are rejected. Wired through --model / create_llm_client
/ subagent model params.

L3 profile temperature: new profile field "temperature" becomes the
client's default_temperature. chat_completion/stream_chat treat
temperature=None as "use profile default, else the old hardcoded
0.3/0.7", so explicit caller values still win. DeepSeekChat and
run_light_subagent resolve CLI > profile > constants.DEFAULT_TEMPERATURE.

L5 reasoning field aliases: _parse_delta recognizes
reasoning_content / reasoning / thinking / thinking_content (first
non-empty wins), so non-DeepSeek vendors' thinking streams are no
longer silently dropped.

L4 (cross-profile failover) intentionally deferred: streaming
failover needs request-replay semantics and is feature-level work.

Tests: new TestLBatch (16 cases); 7 existing unpack sites updated to
the 3-tuple. Full suite 315 passed.
2026-08-18 00:06:23 +08:00
qing a583f44d5d refactor(aibox): make profile reasoning_enabled a constructor param (M3)
Profile-level reasoning_enabled was attached as an instance attribute
after construction only when a profile matched; the base class never
declared it and consumers probed with double getattr. Any code that
accidentally set client.reasoning_enabled would silently override the
global switch.

The flag is now an explicit LLMClient constructor parameter with
three-state semantics: True/False (profile-level explicit, or the
global api.reasoning_enabled value concretized when the profile omits
the field) and None (no profile -> consumer falls back to global).
create_llm_client passes it at construction and no longer mutates the
client afterwards; DeepSeekChat._reasoning_enabled simplifies to a
single getattr probe that still tolerates clients without the attr.

Tests: TestM3ReasoningConstructor (4 cases) covering profile True,
profile-omitted -> global concretized, base default None, and the
None -> global fallback at the consumer; one legacy assertion updated
to the new contract (attribute exists, value None). Full suite 299
passed.
2026-08-17 23:58:45 +08:00
qing 5f3ca9a246 fix(aibox): sync compressor and snapshots on client swap via set_client (M1)
run_full_subagent replaced sub_chat.client by direct assignment after
construction, leaving ConversationCompressor (bound to the original
client in DeepSeekChat.__init__) and the api_key/name snapshots
pointing at the default model — compression requests silently kept
using the default endpoint/model while the dialogue itself used the
requested one.

DeepSeekChat gains set_client(): swaps self.client and refreshes
api_key, name, and compressor.llm_client in one place. run_full_subagent
now routes model overrides through it.

Tests: new TestM1SetClient (2 cases: full reference refresh on a real
DeepSeekChat instance, and run_full_subagent calling set_client with
the new client); full suite 295 passed.
2026-08-17 23:48:40 +08:00
qing 77262f8c3a fix(aibox): inject available model profile names into create_subagent (M2)
The LLM had no way to know which profile names exist in the models
config section; a hallucinated name fell through to bare-name semantics
(default endpoint + bogus model) and failed at request time with API
400.

SubAgentTool now builds the model hint dynamically from config via
_model_names_hint(): description and the model parameter's JSON Schema
description both list the profile names, with the default profile
(api.default_model) marked. Empty or malformed models sections keep
the original wording untouched (zero breakage); exceptions degrade to
no hint.

README: tool table note, test count 293, new v3.4 changelog entry
summarizing the H1/H2/H3/M2 profile-hardening batch.

Tests: new TestM2SubAgentModelHint (5 cases); full suite 293 passed.
2026-08-17 23:42:04 +08:00
qing 84a3f6dd5e fix(aibox): make --model effective in WebSocket server mode (H3)
--model was accepted by parse_args but only consumed inside
handle_once_mode, so `python aibox.py --model kimi` (server mode)
silently ignored the override.

The model now flows as a process-wide override:
main() -> EnhancedAIManager(model=...) -> DeepSeekChat(model=...) in
all three AI creation paths: add_ai (main dialogue),
WebSocketHandler._create_session_ai (per-connection sessions), and
TaskScheduler._create_task_ai (scheduled tasks). Manager logs
"CLI 模型覆盖已启用" at startup for observability. --once mode
plumbing is unchanged; --model=None keeps config-driven behavior.

Tests: new TestH3ModelPlumbing (6 cases) mocking DeepSeekChat to
assert the constructor kwarg across manager/session/task paths plus
parse_args; full suite 288 passed.
2026-08-17 23:37:59 +08:00
qing 0e9efe4895 fix(aibox): validate model profile schema at config source (H2)
Profiles previously had no validation, so config errors surfaced far
from their origin and cryptically:
- explicit profile missing `model` fell back to the profile NAME as the
  model (a profile named "local" would send "model": "local")
- default profile missing `model` with no model param produced
  "model": null -> API 400
- missing `api_url` surfaced as requests InvalidURL

_resolve_model_profile now runs _validate_profile (dict type + api_url)
before reading fields and _require_model on the effective model,
raising ValueErrors that name the profile, the missing field, and the
config.json path to fix. Bare model names still legally substitute for
a default profile's missing model field.

Tests: new TestH2SchemaValidation (6 cases: missing model/api_url,
null-model via default profile, bare-name substitution, non-dict
profile, e2e create_llm_client); full suite 282 passed.
2026-08-17 23:31:06 +08:00
qing 4480bce684 fix(aibox): never fall back to deepseek.key for model profile endpoints (H1)
~/.cert/deepseek.key is a DeepSeek-only credential; when a models profile
specified its own api_key_env (e.g. MOONSHOT_API_KEY) but the env var was
unset, resolve_api_key silently read that file and sent the DeepSeek key
as Bearer to a third-party endpoint (cross-vendor credential leak).

resolve_api_key gains allow_key_file (default True for the legacy flat
api section); create_llm_client passes allow_key_file=profile is None so
any profile path raises a clear RuntimeError instead, pointing users to
set the env var or use "" for local keyless endpoints.

Tests: new TestH1KeyIsolation (4 cases) with a mocked readable
deepseek.key proving the profile path errors out while the flat api
section still reads the file; full suite 276 passed.
2026-08-17 23:28:25 +08:00
qing 90f5544673 docs(aibox): changelog F2 (registry timeout cleanup + reader finalization)
Batch F bullet now split into F1/F2 entries; F2 covers unified
timeout-path cleanup of tool streaming attributes and reader-thread
finalization with the CPython stream-lock correction; test count 272.
2026-08-17 22:49:45 +08:00
qing 6ef5d11d9d docs(aibox): sync README with Batch E/F security fixes
- feature list & tool table: set_proxy password semantics (memory-only,
  password_env for persistence)
- config example: fix stale flat proxy section to actual proxy.socks5
  structure with password_env; add plaintext-not-persisted note
- changelog: new v3.3 section covering Batch E (command exec protection
  chain) and Batch F (credential protection), test count 267
2026-08-17 22:41:24 +08:00
qing d03dbc7f91 feat(aibox): batch D multi-model profiles + docs
- models 配置节:多 OpenAI Chat Completions 兼容端点按档案名切换,默认空 dict 零破坏回退旧 api 节
- --model 双语义:档案名(命中 models 节完整采用)/ 裸模型名(同端点换模型)
- resolve_api_key 档案级 env 三级语义:未配置回退 api.api_key_env / 显式名称 / 空串免密(Ollama/vLLM)
- 档案级 reasoning_enabled 附加 client 属性,DeepSeekChat._reasoning_enabled 优先读取
- llm_client 增加 openai_compatible/openai provider 别名
- README 更新:aibox/README 六处(特性/配置/多模型档案专节/CLI 参数/更新日志 v3.2/测试计数 264)+ 根 README aibox 小节
- 单测 17 项新增(test_models_profile.py),全套 264 项全过
2026-08-17 16:58:33 +08:00
qing 7f21b65428 fix(aibox): FTS5 中文检索 + Token 压缩阈值生效——Compressor 抽离解耦
- db.py: FTS5 改用 trigram 分词(中文可检索),旧 unicode61 表自动迁移回灌;search_memories 增加 LIKE 兜底(%/_ 转义、MATCH 异常防护)
- conversation_compressor.py: should_compress 补齐 Token 阈值判断(原为死配置),Token 触发时按比例压缩早期消息
- ConversationCompressor 抽离至 subsystems,注入 llm_client 打破与 DeepSeekChat 循环引用;aibox.py 3517→3018 行
- 新增 21 项测试(中文检索/旧表迁移/LIKE 转义/token 估算/压缩流程/失败回退),全量 232 项通过
2026-08-15 09:51:59 +08:00
qing d47ef6efb4 docs(aibox): 同步v3.0模块化文档,修复测试路径漂移
- README.md: 更新行数(8450->3706)、重写目录结构(含tools/subsystems/tests包)、补充包依赖方向与测试运行说明、新增v3.0更新日志
- test_basic_tools.py: 4个ReadFileTool测试改用tmp_path fixture,消除cwd依赖
- 新增 docs/reports/aibox_refactor_progress_20260814.md: 重构进度报告
2026-08-13 16:26:41 +08:00
qing 518c8b2b90 feat(aibox): v2.9 编排者-执行者工具分层(tool_profile)
主对话仅保留记忆/情绪/调度/子代理派生等管理面工具,不再直接持有命令行;
子代理(worker)仅注册 7 个执行面工具,无记忆/人格/派生/提问能力,
修复子代理可改写主人格(update_soul)与递归派生的隐患。
附 v2.9 配套运行期提示词模板(aibox/prompts/)与实施方案文档。
2026-08-13 03:05:47 +08:00
qing 14db64f7a2 refactor(aibox): 工具基类 Tool/ToolRegistry 抽离至 tools 包
延续 common 包的拆分模式,形成 common <- tools <- aibox 单向依赖,
为后续工具子类逐步迁移铺路;aibox.py 瘦身约 230 行,工具子类与注册逻辑零改动。
2026-08-13 02:50:20 +08:00
qing 058eb11f5f docs(aibox): 更新 README 补充 express_emotion 工具与 /search 命令(移除过时说明、命令表新增 /search、工具表新增 express_emotion、消息类型表补充 task_list/task_completed、更新日志新增 v2.8) 2026-08-05 01:51:10 +08:00
qing 53ddcb364f feat(aibox): 创建/安装技能工具统一生成 .md 扩展名 + 下线旧版 smtp.py
- CreateSkillTool/InstallSkillTool 改为生成带 .md 扩展名的技能文件,与人格技能文件规范一致
- 同步更新 7_extension_skills.md 模板与工具描述
- 删除旧版 smtp.py(P0-1)并同步审计文档与 README
2026-08-04 08:26:57 +08:00
qing d24066369b refactor(aibox): 解耦大模型调用,新增 llm_client 抽象层以支持多厂商切换
将 think_and_respond 与 _compress_via_llm 中的直接 DeepSeek API 调用
收敛到 LLMClient 抽象层(DeepSeekClient 实现 + create_llm_client 工厂),
统一流式事件格式,为子代理/多模型切换预留扩展点;配置新增 api.provider。
2026-08-03 23:03:03 +08:00
qing 417fda5479 refactor(aibox): 文件/网络操作工具改为返回命令行指引并移除beautifulsoup4依赖 2026-08-03 22:52:12 +08:00
qing 9d737897df fix(aibox): v2.6 - 修复Ctrl+C中断无法停止服务器的问题
问题根因:
1. WebSocketHandler.handle_message 完全没有处理 interrupt 消息类型(前端Ctrl+C发送的消息被丢弃)
2. DeepSeekChat 缺少取消机制,阻塞的 API 请求(iter_lines)和子进程无法被中断

修复方案 - 三层取消机制:
1. threading.Event 取消标志:流式响应循环每轮检查 _is_cancelled(),工具调用前检查
2. HTTP连接关闭:保存 _current_response 引用,cancel() 时调用 response.close() 中断 iter_lines()
3. 子进程终止:SubprocessCommandExecutor 添加 cancel() 方法,保存 _current_process,cancel时kill

修改内容:
- DeepSeekChat: 添加 _cancel_event/_current_response/_command_executor 字段,cancel()/_is_cancelled()/_check_cancelled() 方法
- SubprocessCommandExecutor: 添加 __init__/_process_lock/cancel(),execute()/execute_streaming() 中设置和清除进程引用
- WebSocketHandler: 添加 handle_interrupt() 处理 interrupt 消息,回复 interrupt_ack 后调用 ai_manager.cancel()
- EnhancedAIManager: 添加 cancel() 统一入口,同时取消AI和命令执行器
- think_and_respond: 首轮清除cancel标志,流式循环检查取消,API/JSON/未知异常中均检查取消标志
- _handle_tool_calls: 每个工具调用前检查取消信号
- 更新README v2.6日志和API文档
2026-08-01 14:09:24 +08:00
qing b79044d631 feat(aibox): add DeepSeek reasoning mode support with tool calling on deepseek-v4-flash
- Add api.reasoning_enabled config (default false) to enable thinking mode
- Uses deepseek-v4-flash native thinking capability (thinking param), supports tool calls while reasoning
- Real-time reasoning content streaming via reasoning_start/reasoning/reasoning_end WS messages
- Frontend shows reasoning in gray italic area, auto-folds long reasoning (>2000 chars)
- Dynamic reasoning detection: shows reasoning even if model returns it without explicit config
- Increase default API timeout to 120s for longer reasoning responses
- Remove duplicate _add_assistant_message_with_reasoning method
- Update docs: config example, message types table, v2.5 changelog
2026-08-01 04:33:55 +08:00
qing bd544a4181 docs(aibox): update file line count to ~8450 2026-08-01 03:51:17 +08:00
qing 164bc65b95 feat(aibox): add dedicated workspace directory to prevent file pollution
- Add WORKSPACE_DIR constant pointing to ~/.aibox/workspace by default
- Update file operations prompt to require writing temp files to workspace
- Update command execution prompt to use workspace as default work_dir
- Add command output size limit note in command execution prompt
- Update README with workspace directory structure and specification
- Add system.workspace_dir config item documentation
- Add v2.4 changelog entry
2026-08-01 03:49:25 +08:00
qing 2f3e63e499 fix(aibox): prevent OOM from large tool outputs with multi-layer safeguards
- ReadFileTool: replace readlines() with on-demand range reading; add reverse block reading for large file tail; limit single line to 10KB; hard-cap max_lines to 500
- SubprocessCommandExecutor: replace capture_output with line-by-line Popen reading; enforce 1MB output limit with immediate process termination; merge stdout/stderr counting for both sync and streaming modes
- Raise TOOL_MAX_RESULT_LENGTH default from 10KB to 1MB as final safety net
- Add SECURITY_AUDIT.md documenting vulnerabilities and remediation roadmap
- Update README with memory safety features and v2.3 changelog
2026-08-01 03:46:02 +08:00
qing 3be9e78335 docs(aibox): update README for v2.2 prompt system fixes
- Expand prompts directory listing to show all 10 skill files explicitly
- Update prompt system table: '1_*.md' → 'N_*.md (N=1~10)' with numeric ordering
- Document load order guarantee (identity → skills 1-10 → soul → status)
- Document is_continuation behavior (no repeated pre_prompt on recursive calls)
- Add v2.2 changelog: prompt ordering fix, token waste reduction,
  recursion pre_prompt dedup, large-file threshold, memory search tuning
2026-08-01 03:24:39 +08:00
qing ae42ab8a91 docs(aibox): update README for v2.1 robustness fixes and correct entry filename
- Fix all references from main.py to aibox.py (actual entry file)
- Update installation to use pip install -r requirements.txt
- Expand WebSocket message types table: add line/task_error/task_added/
  notification/connection_ack/interrupt_ack, correct chunk/complete/error semantics
- Fix message flow example: tool calls are reported via 'line' messages,
  not a non-existent 'tool_call' type
- Document _sanitize_tool_result() and error recovery (_rollback_messages)
  in development guide
- Add FAQ entry for tool-call-induced API failures
- Add v2.1 changelog covering all robustness fixes (reasoning_content
  filtering, tool_call id fallback, control character sanitization,
  message rollback on API failure)
2026-08-01 03:09:34 +08:00
qing 254fbf4628 docs(aibox): 更新 README.md 并新增 README.robot.md 技术手册
README.md 修复:
- 新增 ask_user 工具到工具列表
- 补充配置表中缺失的 17 个字段(system/scheduler/api/context/memo/files/commands/web/memory/conversation/tools/compressor/proxy)
- 补充对话压缩完整配置项

README.robot.md 新增:
- 17 章节完整技术手册(~500 行)
- 架构图、数据流、协议详情
- 19 个工具的完整实现信息
- 配置参考(含所有字段说明)
- 已知限制与安全建议
- 部署与故障排查指南
2026-07-30 12:08:17 +08:00
qing ee44e1bef1 docs & ui: 完善 captcha/aibox 文档,登录注册改为邮箱制
- captcha/README.md: 补充字段级接口文档与错误码表
- aibox/README.md: 对照 main.py 源码核对修正(入口/模型/工具/配置)
- static/public/login.html & register.html: 用户名改为邮箱
- static/public/js/register.js: 合并注册页双邮箱框为单一邮箱字段
2026-07-27 22:51:57 +08:00
qing 1b60ab0a2e 增加运维机器人单元 2026-07-27 21:39:53 +08:00