Blizzard
|
8f619c2a62
|
test(dispatcher): 引擎主链路集成测试(pool 抽接口 + 假替身端到端)
把 Orchestrator.pool 从 *llm.Pool 抽成 LLM 接口(Ready/ChatStream/StreamText/Chat),
*llm.Pool 天然满足、main 不变;从而可注入假模型做端到端测试,不依赖网络/Docker/LLM。
新增 integration_test.go(假 LLM/工具/sink/exec 替身):
- runGraph 分支路由:true/false 边标签精确选路(true 边故意列后)。
- runGraph 工具→agent:工具产出注入 agent 上下文。
- runGraph map fan-out:拆项 → 各章并行撰写 → 多章成稿。
- runGraph 输出护栏:流式 token 中疑似密钥被脱敏。
- handleReport:规划 → 分章撰写 → report_store 存源 → 流含标题/各章 + CompleteStream。
全部 go test -race 通过(修了测试替身 fakeExec 的并发追加竞态;生产 ExecSink 安全)。
至此引擎与报告主链路从"仅手动验证"升级为自动化端到端覆盖。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 10:51:39 +08:00 |
|
Blizzard
|
9b33a62573
|
docs: PROGRESS 对齐实况(记忆抽取已完成勾选 + push 债更新 + 日期)
记忆抽取(9c19bb4)已完成并 push,但 PROGRESS 那两行回退成未勾;push 债行停留在旧值。
按已提交实况校正,并把可观测性纳入。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 10:42:40 +08:00 |
|
Blizzard
|
b6a6875795
|
feat(gateway): 可观测性 —— Prometheus 指标 + 结构化日志 + 探针
往"生产可运维"推一步(网关前门):
- Prometheus /metrics:sundynix_http_requests_total{method,route,status}、
request_duration_seconds 直方图、requests_in_flight。route 用 c.FullPath()
路由模板(/tasks/:id/...)避免按真实路径高基数。
- 结构化访问日志:slog JSON 到 stderr(request_id/method/route/status/latency_ms/
ip/uid/bytes),替代 gin 默认文本日志;gin.New()+Recovery 自管中间件链。
- RequestID 中间件:生成/透传 X-Request-ID,写上下文+响应头,供日志关联。
- 探针:/healthz(liveness,不查依赖)、/readyz(readiness,DB+Redis 就绪才 200,
否则 503),供 k8s 等导流判断;/api/v1/health 深度聚合保留。
- 三个根端点不挂业务鉴权(/metrics 生产应由网络层限制抓取来源)。
验证:单测(计数 +1 / X-Request-ID 生成与透传);实跑 /healthz 200、/readyz 200
(db,redis ready)、/metrics 输出真实指标、访问日志 JSON 正常、X-Request-ID 回写。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 10:38:31 +08:00 |
|
Blizzard
|
e05e6f5903
|
fix(gateway): 三处生产安全硬化(默认密钥/admin裸奔/CORS)
1) JWT 默认密钥:生产模式(APP_ENV=production|prod 或 GIN_MODE=release)下若未设
JWT_SECRET 直接 log.Fatal,杜绝用开发默认值签发可伪造令牌;开发期警告并放行。
2) /admin 运维控制面(含模型 API 密钥管理)改挂 RequireAdmin:必须登录 +
(设了 ADMIN_USER_IDS 则)uid 须在白名单;生产期未配置管理员直接 403。
3) CORS Allow-Origin 由 CORS_ALLOW_ORIGIN 配置(缺省 * 仅开发),非 * 时加 Vary。
build + auth 单测通过。仍属"小范围灰度"级,TLS/可观测/集成测试/HA 见 PROGRESS。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 12:55:04 +08:00 |
|
Blizzard
|
aa3139da68
|
feat(mcp-go): external_api 通用出站 HTTP 工具(带 SSRF 防护)
新增 external_api 工具(GET/POST):agent 图可调外部 API。安全为先:
- SSRF 防护 validateExternalURL/isBlockedIP:scheme 限 http/https;拒环回/内网
/链路本地(含 169.254.169.254 云元数据)/未指定 IP;重定向同样校验、限 3 跳。
- 可选 EXTERNAL_API_ALLOWLIST(逗号分隔主机,支持子域)收窄到白名单。
- 超时 10s + 响应体限 256KB。
- 校验逻辑纯函数,单测覆盖(内网/元数据/scheme/白名单,字面量 IP 离线判定)。
注册进 mcp-go dispatch(external_api → externalAPI)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:58:45 +08:00 |
|
Blizzard
|
6523323a27
|
feat(gateway): MinIO 孤儿对象 GC(重名覆盖后清理旧对象)
大文档正文存 MinIO,重名再入库若转内联或换键,旧对象会成孤儿泄漏。
SaveDoc 改为返回 (id, oldObjectKey);runIngest 在覆盖成功后,若旧键非空且
与新键不同,从 MinIO 删除旧对象。新建文档 oldObjectKey 为空,不触发。
注:当前无文档删除/改名端点,主要孤儿路径=大→内联覆盖,已覆盖;
真机 MinIO GC 验证待后续(逻辑直白,blob.Delete/SaveDoc 已分别验证)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:56:22 +08:00 |
|
Blizzard
|
b7360439ab
|
feat(dispatcher): 输出护栏 —— 发射层脱敏疑似密钥/令牌
补齐 Harness 输出侧:harness.RedactSecrets 识别并脱敏 sk-/AKIA/JWT/Bearer 等
疑似密钥令牌(纯逻辑 + 单测)。runAgent 在每个 token 分片发射前调用(流式无法回收
已发,故逐片脱敏),脱敏会累计进 b.answer(写回历史也是脱敏版);有命中则在
运行·观测打一条'已脱敏 N 处'轨迹。
注:跨分片的密钥可能漏(流式现实),逐片为最佳努力;生产可加滑窗缓冲增强。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:52:53 +08:00 |
|
Blizzard
|
718140239d
|
fix(dispatcher): 报告 LLM 调用加单次超时上限,治偶发卡死
之前 writeSection/planOutline/planItems 的 pool.Chat 用无 deadline 的 ctx,个别
DeepSeek 流连接挂住会一直占着(曾累积把整篇卡死)。给每次 LLM 调用套 60s 超时
(llmCtx):超时即 cancel → 底层 http 请求中断 → Chat 返回错误 → 走兜底,不无限等。
happy path(约18s 完成)不变,仅加上限。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:47:46 +08:00 |
|
Blizzard
|
5ec558bf81
|
build: 加 make test/test-go/test-web/test-py 一键测试目标
- test-go: 各 Go 模块 go test(DB 集成测试无 MEMORY_TEST_DSN 自动跳过)
- test-web: 前端 tsc 类型检查
- test-py: mcp-py 沙箱守卫测试(pytest 缺则用内置 harness 兜底)
- test = test-go + test-web
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:44:31 +08:00 |
|
Blizzard
|
cad5b14382
|
feat(mcp-py): 代码沙箱落地 —— AST 静态守卫 + Docker 隔离执行(弃用桩)
mcp-py 的 run_code/secure_sandbox 此前全是桩。落地两层防御:
1) 静态守卫 sandbox.SecureSandbox.static_guard(纯 AST,执行前第一道)
- 拦危险导入(os/sys/subprocess/socket/ctypes/pickle/requests…)、危险调用
(eval/exec/compile/__import__/open…)、逃逸属性(__subclasses__/__globals__…)、语法错误。
- 返回 (放行, 原因)。
2) 隔离执行 interpreter.CodeInterpreter.execute(Docker,真隔离)
- network_disabled 禁网;user=65534 非 root + cap_drop=ALL + no-new-privileges;
read_only 根 + /tmp tmpfs;mem/memswap(禁swap)/nano_cpus/pids_limit 限资源;
python -I 隔离模式;wait 超时即 kill;容器一次性 remove。
- 无 Docker SDK/daemon 时 available()=False 优雅降级,不阻断服务。
gateway:run_code(标准档 256m/0.5cpu/10s) 与 secure_sandbox(紧档 128m/5s) 均走
守卫→隔离,结果整理为 stdout/stderr/exit 可读文本。pyproject 启用 docker 依赖。
验证:
- 守卫 6 单测(放行安全码 / 拦危险导入·调用·逃逸属性 / 语法错误)全过。
- 隔离 4 项实跑(真 Docker):sum(range(10))→45 exit0;非root uid=65534;
禁网 urlopen 失败(DNS解析错);while True 超时 3s 被 kill。
- 无 Docker 降级测过。
生产加固:可把执行运行时换 gVisor(runsc)/Kata(已在注释/PROGRESS 标注)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 11:26:08 +08:00 |
|
Blizzard
|
9657a07bb5
|
feat(auth): 鉴权片2 —— 前端登录闭环 + 保护路由 + 去掉 header 兜底
把 JWT 鉴权从后端核心闭环到端到端:
后端:
- middleware.RequireAuth:上下文无已验证 uid 则 401;挂在 owner 作用域业务路由组。
- 路由拆 公开/受保护:公开=auth/health + 按 task_id 寻址的 SSE 与报告导出
(EventSource/下载无法带 Bearer);受保护=tasks/memory/kb*/agents/reports/billing。
- userID(c) 去掉 X-User-ID 兜底,仅信任 JWT 注入的 uid。
- 修 CORS:Allow-Headers 增 Authorization(否则浏览器拦截带 Bearer 的请求)。
前端:
- lib/api:token 存 localStorage + Bearer 头(不再发 X-User-ID)+ authRegister/Login/Me
+ 401 清令牌并广播 sdx:logout;submitTask/report/memory/列表加载走 Bearer 与 401 守卫。
- views/Login:登录/注册全屏门。
- App:启动校验令牌 → 无则渲染 Login,有则进主应用;identity.userId=已验证 user.id;
监听 sdx:logout 回登录页。
- TopBar:去掉可编辑身份输入,改显登录用户 + 登出。
实跑验证(docker+gateway+preview):
- RequireAuth:无 token /kb/list、/agents → 401;/health → 200;带 token → 200。
- 前端:无 token 显登录门;注入有效 token 重载 → 进主应用、顶栏显 Dexter、KB 加载本人库、
隔离徽标显雪花 uid。控制台无错、生产构建通过。
- 过程中发现并修复 CORS 缺 Authorization 头的真实 bug。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 16:32:00 +08:00 |
|
Blizzard
|
149c35c21b
|
feat(gateway): 真实鉴权片1 —— JWT 注册/登录 + 校验中间件(后端核心)
替掉"裸 X-User-ID 头当身份"的临时方案,落地无状态 JWT 鉴权后端:
- internal/auth:JWT 签发/校验(HS256,密钥 env JWT_SECRET,仅接受 HMAC 防 alg 混淆)
+ bcrypt 密码哈希/校验。纯包,含单测。
- User 模型加 Name + PasswordHash(json:"-" 不外泄);store 加 CreateUser/GetUserByEmail/
GetUserByID(邮箱唯一冲突 → ErrUserExists)。
- handler/auth:POST /auth/register(建用户+签发)· POST /auth/login(校验+签发,
用户不存在与密码错同一文案防枚举)· GET /auth/me。
- middleware/auth:解析 Bearer JWT,校验通过把已验证 userID 注入上下文(非阻断)。
- userID(c) 改为优先取 JWT 注入的 uid,兜底 X-User-ID 头(前端尚未接登录,保持可用)。
验证:
- 单测:JWT 签发/解析往返、过期拒绝、篡改/非法拒绝、bcrypt 哈希校验。
- 实跑(nats+pg+gateway):注册→token+user(无密码)、重复注册 409、错密码 401、
/auth/me 带 token 200 / 无 token 401;owner 隔离改用已验证 uid —— 带 token 建的库
匿名/伪造 header 都看不到(JWT 用户数据归于雪花 id,header 无法臆测)。
片 2 待做:前端登录页 + 存令牌带 Bearer + 处理 401 + 去掉 header 兜底 + 保护路由。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 16:14:21 +08:00 |
|
Blizzard
|
3ae009db38
|
feat(dispatcher): LLM 自动化评测落地(规则 + LLM-as-judge)+ 单测
Evaluator 此前是空桩(Score 恒返 0)且未接线。落地为真实自动化评测并接入:
- 规则评测(always-on,纯函数):空输出/过短/疑似拒答/重复啰嗦各扣分 → 0–1 分 + 标签。
- LLM-as-judge(模型就绪时):让模型对(输入,输出)按相关性/准确性/完整性 1–5 打分给理由,
归一化后与规则分加权(0.4 规则 + 0.6 LLM);解析失败/无模型则回退纯规则分。
- 经注入 ready/chat 解耦 LLM 后端,便于单测(无需真实模型)。
- 接线:orchestrator 在答复产出后 `go o.evaluate(...)` 异步评分并记日志(off 热路径,
不影响响应与流式);main.go 用 pool.Ready/pool.Chat 构造 Evaluator。
测试:规则各情形(正常/空/过短/拒答/重复)、纯规则模式、LLM-judge(带围栏 JSON 解析 +
归一化 + 加权)、坏 JSON 回退 —— 全过。
至此 Harness 三件:熔断降级 ✅ · 输入护栏 ✅ · LLM 自动化评测 ✅(输出护栏待 emit 层)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 15:32:02 +08:00 |
|
Blizzard
|
e63632adf5
|
feat(gateway): 输入护栏拦提示词注入/超大体(弃用空桩)+ 单测
Guardrail 中间件此前是空桩(直接 c.Next)。落地输入护栏:
- 新增纯逻辑包 internal/guardrail:Inspect(body) 检测提示词注入(忽略既定指令/
角色越权/诱导泄露提示词,中英文模式)+ 超大体(>256KB),与 HTTP 解耦便于单测;
敏感词黑名单留空可扩展。
- 中间件:仅对带 JSON 体的 POST/PUT 检查(文件上传 multipart 与 GET/SSE 跳过);
限读 + 命中拦截返回 422;未命中则还原请求体(io.NopCloser)供 handler 读取。
- 输出护栏不在网关做:Token 流是 SSE 实时流,网关缓冲会破坏流式 —— 标到路线图,
应在 dispatcher token 发射层做。
验证:
- 单测:正常输入不误拦、中英文注入均拦、超大体拦、边界恰好放行。
- 实跑(nats+gateway):注入(中/英) → 422 带原因;干净输入 → 202 且 body 正确还原、
handler 正常发布到 NATS。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 15:19:14 +08:00 |
|
Blizzard
|
31bf3e5907
|
feat(dispatcher): 熔断降级真三态状态机(弃用空桩)+ 单测
CircuitBreaker 此前是空桩(Allow 恒 true、Report 空操作),dispatcher 调 LLM/工具
无任何失败保护——今天就撞上 DeepSeek 流连接累积把报告卡死。改为真实三态熔断:
- Closed:正常放行;连续失败达阈值(默认5) → Open。
- Open:快速拒绝;冷却(默认10s)到点 → HalfOpen 放行少量探测(默认1)。
- HalfOpen:探测成功 → Closed 恢复;探测失败 → 重新 Open。
- sync.Mutex 并发安全(多任务 goroutine 共享);时钟可注入便于确定性测试。
orchestrator.Handle:熔断开启时不再静默丢弃任务,改为回流"服务繁忙"提示 +
CompleteStream 收尾,让客户端解阻不挂死。
测试(含 -race):达阈值断开、成功清零、半开恢复、探测失败重断、并发安全 —— 全过。
PROGRESS.md 勾掉熔断项。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 15:02:45 +08:00 |
|
Blizzard
|
15606a6570
|
docs: 新增 PROGRESS.md 进度清单(按架构分层的已做/未做活文档)
对照 architecture.md 5 层 + 功能规划,列出已完成/部分/未实现,复选框形式,
完成一项勾一项,方便记忆与续作。基于当前代码实况(至提交 79f9912)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 14:58:58 +08:00 |
|