Maintainer edit: make token truncation include the suffix within the budget and route the consolidator through the shared helper so recent-history and archive truncation keep the same semantics.
The recent-history section injected into the system prompt was capped by character count (_MAX_HISTORY_CHARS = 32_000). Characters are a poor proxy for tokens: ~32k chars of English is ~8k tokens, but the same char count of CJK text or code can be far more, so the cap could let the section blow well past its intended size on non English/code-heavy histories.
Add a reusable truncate_text_to_tokens() helper (reusing the tiktoken
cl100k_base encoder already used elsewhere, with a char-based fallback) and
switch the digest cap to a token budget (_MAX_HISTORY_TOKENS = 8_000),
matching the previous English-text size while holding regardless of
content.
The non-streaming retry called process_direct again with the same
content, persisting a duplicate user turn. Pass persist_user_message=False
so the retry recovers a response without re-recording the user message.
* fix(api): forward real LLM usage in /v1/chat/completions response
_chat_completion_response() hardcoded prompt_tokens/completion_tokens
to zero. Now reads agent_loop._last_usage (set by process_direct
after every LLM call) and forwards the actual prompt/completion counts.
Streaming path is unchanged; usage is only surfaced in non-streaming
responses for now.
Fixes#4309
* fix: use defensive getattr for _last_usage and add it to all test mock agents
- Use getattr(agent_loop, '_last_usage', None) in server.py for safety
- Add _last_usage = {} to mock agents in test_api_attachment.py and test_api_stream.py
- Prevents AttributeError/500 when mock agents don't have the attribute
* fix(api): preserve provider total usage
---------
Co-authored-by: michaelxer <michaelxer@users.noreply.github.com>
Co-authored-by: Xubin Ren <52506698+Re-bin@users.noreply.github.com>
Maintainer edit: subagents rebuilt their scoped ToolsConfig without carrying tools.file, which re-enabled built-in file tools after the parent agent disabled them. Preserve the file config and add loader/subagent coverage for the disabled path.
Avoid resubmitting unchanged schedules from the automation edit dialog so completed one-time automations can still have their content updated. Treat unchanged schedules as already-valid on the backend while preserving validation for actual schedule changes.
Maintainer edit: redact external channel chat identifiers from the WebUI automation payload and reject malformed or unschedulable automation updates before they mutate cron jobs.
The temperature suppression was hardcoded to only match opus-4-7. Newer
Anthropic models (opus-4-8, fable) also reject the parameter with a 400.
Normalize model_name to lowercase before matching so mixed-case configs
do not fall through. Add tests for opus-4-8 and fable across adaptive,
enabled, and no-thinking paths, plus a negative test confirming ordinary
models still send temperature.
Fixes#4333
Idle compaction summarized only the dropped prefix, excluding the recent
suffix it retains. On a finished conversation a late user correction or
final result lands in that kept suffix, so it never reached the persisted
summary and history kept the stale pre-correction conclusion — which, for
idle sessions that are rarely resumed, is never fixed.
Summarize over the full unconsolidated tail instead, while still removing
(and raw-dumping on LLM failure) only the dropped messages. Adds an opt-in
summary_context argument to Consolidator.archive so the summarization
window and the archived set can differ without affecting other callers.
maintainer edit: reject arbitrary custom provider keys that normalize to built-in provider names so runtime and WebUI settings cannot disagree about whether a provider is dynamic or built in.