Streaming chat ran render tools on an async thread with no bound request,
so download links lost their host and arrived without a domain. Resolve the
host on the request thread and carry it through ChatOrigin/ToolContext;
falls back to a configurable public-base-url, then a relative path.
Group-chat reply slot fallback:
- The platform blocks proactive sends in group chats; outbound paths
(cron summaries, async-task completions, generated image/music/3D
delivery, TTS audio) silently failed because they fell through to
the proactive-send command. New bounded LRU maps each group chat
to its most recent inbound frame id; the dispatcher prefers that
reply slot and falls through to proactive only for single chats.
- Centralised text and media dispatch through a single helper so the
group rule never has to be re-implemented per outbound path.
Upload size pre-check + auto-downgrade:
- Without client-side limits, oversized uploads streamed for ~1 minute
before the server rejected at the finish step — users saw nothing
arrive in their chat. The new decision layer mirrors the platform's
hard limits and produces three outcomes: rejected with a friendly
reason, downgraded to a generic file delivery with an inline note,
or pass-through unchanged.
- Files over 20MB reject. Images / videos over their 10MB limit
downgrade to file. Voice content that isn't AMR or exceeds 2MB
downgrades. AMR voice within 2MB stays native.
appmsg inbound parsing:
- Forwarded complex messages (document transfers, article links,
miniprogram cards) used to fall into the inbound switch's default
branch and silently drop. The new branch flattens four sub-types
into a text marker the agent reads plus any media that needs to
reach downstream tools — document forwards reuse the same magic-
byte sniff and per-conversation upload layout as native file
inbound, so extension recovery and chat-uploads serving work
identically.
- Article links produce "[链接] title\ndescription\nurl" so the agent
can summarize without round-tripping. Miniprograms surface their
title. Unknown sub-types still emit a generic marker so the agent
is never blind.
WeCom quoted-message context:
- Parse the body.quote field that arrives alongside any inbound message
(text / image / voice / file / mixed sub-types). When a user long-
presses a previous bot bubble and types a follow-up like "解释一下",
the agent now sees both the user's new text and the referenced
content as proper context — replies stay on topic instead of
guessing what was being explained.
- Quoted images / files are downloaded through the same pipeline as
inbound new media (magic-byte sniff, ZIP container peek for
DOCX / XLSX / PPTX recovery, chat-uploads layout) so the vision
sidecar and document tools can actually analyse what was quoted.
- Reading order in the assembled prompt: "[引用消息: ...]\n<user text>"
first, then quoted media parts, then the user's own current-message
media. Mixed quotes flatten into a space-joined summary.
Multimodal sidecar settings preservation:
- The bulk settings PUT used to unconditionally overwrite the vision /
video sidecar model ids — null in a partial payload became "" in
the DB, silently wiping the configured sidecar every time a user
saved an unrelated settings page (System / Music / Image / etc.).
Symptom: "I picked a vision model, saved a different settings tab,
now the bot can't see images anymore."
- Bulk save now guards both keys with non-null checks, matching the
pattern used for music / 3D / image / video / tts / stt blocks.
- A dedicated /settings/sidecar endpoint always writes both keys, so
the sidecar UI can still explicitly clear via null without leaking
the write-on-null semantics into every other settings save.
- Frontend sidecar card switches to the dedicated endpoint; other
settings pages keep their existing partial-payload behaviour.
Full-stack AI assistant built on Spring AI Alibaba.
Features: ReAct Agent, Plan-and-Execute, MCP Protocol, Multi-Model, Multi-Channel.
Apache-2.0 License