86 Commits

Author SHA1 Message Date
姚章浩 70b144f1e5 fix(weknora): drop 120s client total timeout so long agent streams are not cut
The weknora HTTP client used a hardcoded 120s total-duration timeout, which
kills any smart-reasoning agent stream that legitimately runs longer (22 tool
rounds measured in prod) and the failure branch then overwrote the already
streamed card with the error text. Pass 0 and keep Go's default (no timeout).
2026-09-22 16:10:07 +08:00
姚章浩 1bbaf2f07a fix(outboundtext): keep resource handles intact when normalizing formulas 2026-09-18 17:14:45 +08:00
姚章浩 48b741984f fix(reasoning): never cut a lead-in that carries an image 2026-09-18 16:49:16 +08:00
姚章浩 5ccaeb3b96 fix(outbound): keep single-run answers whole and show formulas
The DingTalk binding can point at the quick-answer agent, which publishes the
whole answer as one answer run plus tool_call/tool_result steps. The narration
boundary was rejected for that shape (start=0), so the text-only heuristic ran
and dropped the answer lead-in together with its image.

- answer_observation: a single answer run means there is no narration to cut.
- outboundtext: rewrite LaTeX formulas into plain Unicode text, because
  DingTalk cards render no math typesetting (r = sqrt(X^2 + Y^2) -> r = √(X² + Y²)).
- dingtalk handler: normalize formulas on both the partial and final card paths.
2026-09-18 16:21:42 +08:00
姚章浩 dfce8cb9f6 test(qa): pin the answer-run boundary used to trim narration
Adds the production CRA5 shape (narration run + tool steps + final answer run
that opens with a lead-in and an image) and a control case where the last
answer run is interrupted by a tool call and the heuristic still applies.
2026-09-18 15:58:14 +08:00
姚章浩 149e94b34b fix(reasoning): keep the answer body's lead-in and images when trimming narration
The DingTalk card dropped the answer's opening (and any image reference in
it) whenever the agent narrated its steps before answering: the text-only
boundary heuristic (reasoningmode.BodyStartOffset) treats everything before
the first structural line as narration, so a lead-in like "我已经找到了…"
plus the image that follows it disappeared, leaving the card to start at
the first heading.

The event stream already knows the boundary: every agent step publishes its
answer chunks under its own answer run id (data.event_id), and the final
body is the last run that no tool/thinking event interrupts. WeKnora
StreamEvent now carries the run id and run start offset; qa.Service tracks
the observation and prefers it over the heuristic, falling back to the
heuristic when the stream carries no run information.

Verified: production turn efae317e (给我CRA5的负载图) lost 131 chars —
lead-in + the load-curve image + separator — and now keeps them.
2026-09-18 15:53:43 +08:00
hermes dd229ec0ad feat(dingtalk): per-frame wide-table grouping (stream keeps tables flowing while streaming) 2026-09-18 11:55:45 +08:00
hermes 92324b4242 feat(dingtalk): make wide-table grouping switchable for wide-screen PC cards
DINGTALK_CARD_WIDE_TABLE_GROUP=off keeps over-wide answer tables as tables on the final frame (still replaces <br> inside cells) so the PC wide-screen card can lay them out itself.
2026-09-18 11:45:55 +08:00
agent 882ac62433 perf(dingtalk): compress outbound screenshots, reuse uploaded objects, raise rewrite budget
- outboundimage: re-encode large PNG/JPEG screenshots as downscaled JPEG (1600px long edge, q82, transparency flattened onto white) before upload; undecodable, small, GIF/WebP payloads are published untouched
- outboundimage: StableHashPublisher skips the upload when the content-addressed object already exists (ObjectStater); OSS store implements it via IsObjectExist
- cmd/dobotai: DOBOTAI_OUTBOUND_IMAGE_REWRITE_TIMEOUT default 60s -> 180s; DOBOTAI_OUTBOUND_IMAGE_COMPRESS (on by default, off restores raw bytes)
2026-09-18 11:14:13 +08:00
agent 8a9c1eaf82 perf(dingtalk): rewrite answer images concurrently and make the rewrite budget configurable
- outboundimage: fetch and publish up to 4 images in parallel, dedupe in document order, log per-image outcome plus a rewrite summary
- dingtalk handler: OutboundRewriteTimeout applies to the image rewrite stage only, falling back to finalDeliveryTimeout
- cmd/dobotai: DOBOTAI_OUTBOUND_IMAGE_REWRITE_TIMEOUT (default 60s, invalid values fall back)
2026-09-18 10:57:32 +08:00
姚章浩 cfe3271fa2 feat(dingtalk): fall back to the public image URL and report image ref shape
The loopback fetch now degrades to the public URL ...
2026-09-17 18:20:02 +08:00
姚章浩 dfc7c8dba9 feat(dingtalk): deliver card images through DingTalk media ids
AI card bodies render only DingTalk-hosted media, so published image URLs are
rewritten into media ids inside card content (streaming frames never block,
final frame waits briefly), while the markdown fallback keeps external URLs.
2026-09-17 18:14:03 +08:00
姚章浩 5f1206b545 fix(dingtalk): 推理正文边界支持「首行即正文」并补齐卡片结构诊断 + 流式分帧重放守卫 2026-09-17 14:16:40 +08:00
姚章浩 a1a2aabab4 fix(dingtalk): 推理正文边界支持「首行即正文」并补齐卡片结构诊断
- BodyStartOffset:内容首个非空行即结构行时正文从头开始,避免收尾裁剪
  把标题+首段一起删掉(118 条生产样本中 31 条、最多 526 字)
- 语料回归守卫:隐藏前缀不得含结构行、上限 600 字;单测补 suffix 断言
- 卡片诊断新增 body_ok/body_line/body_prefix_runes/banner_pos/first_heading_line
  (定位流式残留推理末句,只看结构不看文本)
2026-09-17 14:01:10 +08:00
姚章浩 cd3f92aaaa verify: 合并宽屏自适应与推理输出改造(测试服验证用) 2026-09-17 11:03:05 +08:00
姚章浩 15f2a11162 fix(dingtalk): 推理模式卡片隐藏推理并只留正文
- reasoningmode: 新增正文边界识别(结构行 + 前置空行)与流式 PartialFeed:
  分步叙述按行拼接显示,正文开始时只输出正文,卡片更新即刷新清掉推理区
- dingtalk: 事件流与流式回调统一改走 PartialFeed
- qa: 推理执行的最终答案在入库/投递前去掉步骤叙述(快速模式行为不变)

语料回归:生产 118 条内建智能推理答案中 106 条识别出正文边界(被隐藏的
均为 59-164 字过渡句),12 条无推理的问候类答案保持原样;go vet 与
go test ./... 全绿。
2026-09-17 10:58:59 +08:00
hermes-agent a096ba5f51 fix(dingtalk): 投放 AI 卡片时开启宽屏自适应布局
PC 端卡片宽度过窄:创建卡片(createAndDeliver)请求体从未请求宽屏布局。

修复落在创建卡片的 JSON 载荷层 —— cardData.cardParamMap 的钉钉保留系统
字段 sys_full_json_obj 内传 {"config":{"autoLayout":true}}。config 为
对象类型,钉钉规定复杂类型变量必须走该保留字段;放在 cardParamMap 兄弟
节点会因变量未赋值而静默失效(宽度不变、无报错)。

- 仅影响 PC 宽屏渲染;移动端保持自适应布局不变。
- 未改动富文本组件宽度/padding,也未改动模板内静态内容。
- 流式更新(streaming)请求体不带该字段,避免中途变更布局。

新增回归守卫测试:投放体(direct/group)必须含该字段且 autoLayout=true,
流式更新体必须不含;防止退化为静默失效写法。

验证:gofmt 干净、go vet ok、go test ./... 全绿;
A/B 探针(A 带字段 / B 对照)经 PC 端人工确认 A 卡片变宽。
2026-09-17 09:59:39 +08:00
姚章浩 4e65028d1d fix(dingtalk): strip leaked internal source handles 2026-09-15 17:08:33 +08:00
姚章浩 c7d2629a77 fix(coverage): let references override partial no-answer wording 2026-09-15 16:53:12 +08:00
姚章浩 b49cb5eaf9 feat(dingtalk): normalize wide tables into grouped lists on final card 2026-09-15 16:09:43 +08:00
姚章浩 16aa981028 test(reasoning): cover default-mode routing, user pinning and 000022 contract 2026-09-15 14:01:00 +08:00
姚章浩 f800127461 feat(reasoning): support operator-configurable default mode via DOBOTAI_DEFAULT_MODE 2026-09-15 12:25:09 +08:00
姚章浩 d6a781ba5b fix(dingtalk): send answer language via Accept-Language header
WeKnora's system prompt hard-enforces 'ALWAYS respond in {{language}}',
fed by its Language middleware (WEKNORA_LANGUAGE env -> Accept-Language
header -> zh-CN fallback). The previous localizedQuery() prompt prefix
lost against that instruction, so a user-set 'en' preference still got
Chinese answers on English questions.

- AgentChatRequest.Locale is transmitted as the Accept-Language header
  (never in the JSON body); WeKnora maps it to the prompt language
- 'auto' detects the query script client-side (latin->en, Han->zh-CN,
  kana->ja, Hangul->ko); es is never auto-guessed
- unset/unknown locale sends no header, preserving prior server behavior
- drop localizedQuery prompt prefixes entirely
2026-09-14 16:58:52 +08:00
姚章浩 8027635330 fix(coverage): skip alerts for non-knowledge text turns 2026-09-10 22:02:59 +08:00
姚章浩 9abbb2cb2b feat(dobotai): containerize test runtime and admin bootstrap 2026-09-10 18:22:21 +08:00
姚章浩 bfa1b1b25f feat(dingtalk): add reasoning mode and support evidence 2026-09-10 16:24:56 +08:00
姚章浩 0798a2dac7 feat(dingtalk): stabilize multimodal answer workflows 2026-09-09 13:57:43 +08:00
姚章浩 18c81232ce fix: keep only setting command entries 2026-08-14 12:35:33 +08:00
姚章浩 19ed4b50df feat: add guided administrator management 2026-08-14 12:12:33 +08:00
姚章浩 9f65c3eef0 docs: update AI table binding example 2026-08-14 11:46:37 +08:00
姚章浩 50379b3b87 fix: parse AI table iframe query links 2026-08-14 11:40:28 +08:00
姚章浩 3f9ff359b9 feat: accept trusted AI table link tails 2026-08-14 11:20:15 +08:00
姚章浩 a3defdd1c0 fix: report AI table binding validation failures 2026-08-14 11:11:07 +08:00
姚章浩 b4b68d8b21 fix: accept AI table link cards in settings 2026-08-14 10:46:51 +08:00
姚章浩 365f1b8c7d fix: detect missing-product coverage responses 2026-08-14 10:02:58 +08:00
姚章浩 5d68b22636 feat: add operational component status dashboard 2026-08-13 18:33:52 +08:00
姚章浩 6f24743be2 feat: verify and activate support group binding 2026-08-13 18:22:43 +08:00
姚章浩 4dbc20ee74 fix: validate card business responses and trim help 2026-08-13 15:10:09 +08:00
姚章浩 e5be9ee780 fix: stop enqueueing disabled external table exports 2026-08-13 14:49:44 +08:00
姚章浩 0f30f8cec8 fix: route help and repeated-slash settings locally 2026-08-13 14:39:56 +08:00
姚章浩 44d9398c78 fix: keep setup available without active DingTalk app 2026-08-13 13:55:25 +08:00
姚章浩 b207ee4089 feat: switch DingTalk applications with rollback 2026-08-13 13:36:26 +08:00
姚章浩 0fc64ba77d feat: validate and activate AI table bindings 2026-08-13 13:14:06 +08:00
姚章浩 a7432aed49 feat: wire settings admin into DobotAI runtime 2026-08-13 12:59:20 +08:00
姚章浩 5036071ac2 feat: keep table binding messages out of QA 2026-08-13 12:55:00 +08:00
姚章浩 2d9490f424 feat: apply per-user answer language 2026-08-13 12:50:07 +08:00
姚章浩 462ee1f3ff feat: add admin settings control flow 2026-08-13 12:45:08 +08:00
姚章浩 8d2d4b469b feat: add secure settings foundation 2026-08-13 12:38:01 +08:00
姚章浩 9363d10e44 feat: publish outbound answer images to private OSS 2026-08-13 12:34:39 +08:00
姚章浩 74c9af9944 fix: acknowledge DingTalk callbacks before async processing 2026-08-13 12:34:16 +08:00