Commit Graph
223 Commits
Author SHA1 Message Date
chengyongruandXubin Ren b3d3a3e6c3 fix(image): delegate DNS to explicit proxy 2026-07-27 10:06:19 +08:00
chengyongruandXubin Ren d73794bc68 fix(image): honor provider proxy for URL downloads 2026-07-27 10:06:19 +08:00
Xubin Ren cc3dbbe804 fix(security): block IPv6 unspecified SSRF targets 2026-07-27 10:06:19 +08:00
Xubin Ren 4408cde019 fix(security): harden generated image downloads 2026-07-27 10:06:19 +08:00
Xubin Ren cf1e801a29 fix(image): align Gemini hints with model capabilities 2026-07-27 03:07:41 +08:00
Xubin Ren a8604a3172 fix(image): scope Gemini image sizes by model 2026-07-27 03:07:41 +08:00
ef445cc246 fix(image): narrow Gemini Flash aspect-ratio and image-size scoping
Address review feedback that the capability checks were broader than the
documented per-model matrix:

- Drop the extreme aspect ratios (1:4, 4:1, 1:8, 8:1) from the Flash
  allow-list. They are only documented for 3.1 Flash / Flash Lite, so the
  global set could send an unsupported ratio to 2.5 Flash Image or 3.1 Pro
  Image. Keep the ratios common to every Flash image model.
- Identify imageSize support positively via "gemini-3" instead of excluding
  "2.5". The old predicate also matched gemini-2.0-flash-preview-image-
  generation, which (with the default 1K size) altered that model's request
  shape even though only Gemini 3+ image models accept a configurable size.

Add tests for the gemini-2.0 image-size drop and the extreme-ratio drop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 03:07:41 +08:00
4986590bd7 fix(image): pass aspect ratio and size to Gemini Flash image models
The Gemini Flash image path (`generateContent`) dropped both `aspect_ratio`
and `image_size`: `generate()` never forwarded them and
`_generate_gemini_flash` did not accept them, so every request fell back to
1:1 / input-matched output. The Imagen path was unaffected.

Forward the hints and emit them under
`generationConfig.responseFormat.image` per the current Gemini API. Aspect
ratio is validated against the accepted set; `imageSize` is validated against
{512,1K,2K,4K} and only sent to Gemini 3+ image models, since
`gemini-2.5-flash-image` supports only `aspectRatio`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-27 03:07:41 +08:00
Xubin Ren 8e4fe9cfaf fix(providers): preserve non-multimodal tool lists 2026-07-24 18:58:32 +08:00
Xubin Ren 07a81d70be fix(providers): preserve multimodal tool outputs 2026-07-24 18:58:32 +08:00
chengyongruandGitHub aae259c790 feat(webui): simplify model preset settings (#5061) 2026-07-24 00:55:06 +08:00
Arthur K.andchengyongru 3647875aba fix: add one second to retry after delays 2026-07-23 14:10:27 +08:00
chengyongruandGitHub 9cf2fb19c2 feat(xai): surface hosted X Search activity (#5050) 2026-07-23 13:42:09 +08:00
chengyongruandGitHub c7393c785e feat(providers): add xAI Grok OAuth with capability-gated X Search (#5035) 2026-07-23 11:55:16 +08:00
seteiroandGitHub 79d9455313 fix(providers): add Qwen model-level thinking style mapping (#5023)
Add _QWEN_THINKING_MODELS to _MODEL_THINKING_STYLES with enable_thinking style. Prevents Qwen 3.5/3.6/3.7 models from exposing raw reasoning content in chat responses. Closes #4934
2026-07-22 10:46:22 +08:00
yrkandXubin Ren be1cc769d5 docs: refine ModelScope documentation wording 2026-07-22 01:35:20 +08:00
yrkandXubin Ren 9abad4746e feat(providers): add ModelScope provider for LLM and image generation 2026-07-22 01:35:20 +08:00
Kris LuandXubin Ren 89d8c055a8 fix(providers): sanitize UTF-16 surrogates at provider request boundary
Symptom
-------
LLM requests intermittently fail with:
  'utf-8' codec can't encode characters in position N-N+1: surrogates not allowed
when messages contain emoji-heavy content (e.g. HTML with mixed emoji + JSON round-trips).
This blocks the affected session until the session file is quarantined.

Root cause
----------
Surrogate sanitization was only applied at the CLI entry point
(nanobot/cli/commands.py: _sanitize_surrogates). Requests entering
the LLM provider layer through other channels (Feishu, cron, webui,
tool results, memory injection) had no defensive cleaning, so any
message that happened to carry unpaired UTF-16 surrogates (from an
upstream JSON round-trip with ensure_ascii=True on ill-formed input,
memory rehydration, or third-party content) would blow up at
json.dumps -> HTTP encode time inside the provider client.

Fix
---
1. Extract sanitize_surrogates() and sanitize_surrogates_deep() into
   nanobot/utils/helpers.py as the single source of truth. Both use
   utf-16-le round-tripping with errors='surrogatepass' / 'replace',
   so paired surrogates reconstruct back into their real code point
   and lone surrogates collapse to U+FFFD.
2. Make nanobot/cli/commands.py:_sanitize_surrogates a thin wrapper
   that re-exports the shared helper (backward compatible).
3. Add defense-in-depth at the LLM provider boundary in
   nanobot/providers/base.py:_sanitize_empty_content by running
   sanitize_surrogates_deep over each message and its content blocks
   right before requests are serialized to JSON.

Non-goals
---------
- truncate_text() is intentionally left untouched. Python str slicing
  cannot split a single code point into surrogate halves, so it is
  not the source of lone surrogates.
- session/manager storage layer is untouched. Archived sessions
  reproduced the failure only through the request path, not through
  storage.

Verification
------------
- New regression suite tests/providers/test_sanitize_surrogates.py
  covers: paired surrogate reconstruction, lone surrogate replacement,
  identity return on clean input (zero allocation), deep recursion on
  dict/list/tuple, provider _sanitize_empty_content integration, and
  full utf-8 encodability of the sanitized request body.
- 14/14 new tests pass; full existing test module also green.
- Replayed 58 archived real session messages plus adversarial
  lone-surrogate injection through the provider path with no encode
  errors after the fix.

Impact
------
- No behaviour change for clean inputs (sanitize_surrogates_deep is
  an identity return when no surrogate is present).
- Fails-safe: unpaired surrogates degrade to U+FFFD instead of
  aborting the entire request.
2026-07-21 19:17:58 +08:00
chengyongruandchengyongru 1d7bad3909 feat(providers): support Codex fast mode 2026-07-21 17:55:17 +08:00
Xubin Ren b46e7f4377 fix(config): invalidate fields with missing env refs 2026-07-21 17:35:16 +08:00
Ben LenartsandXubin Ren 4cfc99f4b3 fix(transcription): resolve ${VAR} env refs in transcription api_key/api_base
config.loader.load_config() intentionally returns the raw config with ${VAR}
references intact — env interpolation is a separate, explicit step
(resolve_config_env_vars) so that settings read/edit/save paths never
materialize secrets to disk or to the UI.

The transcription config path does not apply that step: both
channels/base.py (channel voice notes) and webui/transcription_ws.py (WebUI
recording) build their effective config via
resolve_transcription_config(load_config()). As a result a configured
api_key of "${GROQ_API_KEY}" (the documented way to reference secrets) is
passed to the provider verbatim, which fails with 401 Invalid API Key. No
amount of rotating the real key helps, because the literal placeholder
string is what gets sent.

Resolve the reference at the single choke point both callers share —
_resolve_transcription_api_key / _resolve_transcription_api_base — using a
new lenient loader.resolve_env_refs() helper (unset var -> empty string, so
a missing variable degrades to "not configured" rather than raising or
leaking). This fixes both entry points at once and cannot drift the way a
per-call-site fix does. Resolving inside load_config() was rejected: the
~20 settings-UI callers depend on it returning raw ${VAR} placeholders.

Literal keys are unaffected; the settings API only reads the derived
`configured` flag (never the key), which now reflects the resolved value.

Claude-Session: https://claude.ai/code/session_01Q3HuVaJAAQJA3kgVQVJ2Zt
2026-07-21 17:35:16 +08:00
bingqilinweimaotaiandXubin Ren 8c68c6fe1e feat: support Kimi K3 2026-07-18 17:38:32 +08:00
bingqilinweimaotaiandchengyongru 8a48af7c74 fix(providers): omit Kimi K2.5/K2.6 temperature 2026-07-17 22:39:33 +08:00
chengyongruandchengyongru 681edfa6f3 fix(providers): honor Codex proxy config consistently 2026-07-15 20:01:48 +08:00
chengyongruandchengyongru 2116e32013 test: speed up CI and harden the suite 2026-07-15 00:18:37 +08:00
chengyongruandGitHub 07f54c25e3 chore(codex): identify failing request stage (#4929) 2026-07-14 22:40:10 +08:00
chengyongruandGitHub 234e895e5a fix(codex): align OAuth defaults and setup docs (#4910)
* fix(codex): align OAuth defaults and setup docs

* docs(codex): clarify provider settings key

* docs(codex): simplify OAuth setup guidance
2026-07-13 17:04:33 +08:00
hamb1yandXubin Ren 400369f0b0 feat: support canonical opencode provider 2026-07-06 12:13:00 +08:00
hamb1yandXubin Ren b8d7708171 fix: normalize text tool call markup 2026-07-06 12:11:08 +08:00
axelray-devandXubin Ren 28011413bc fix(copilot): guard token refresh with asyncio.Lock to prevent race condition
_get_copilot_access_token had a check-then-act race: concurrent chat()
calls after token expiry both fetched new tokens and clobbered each other.
Add asyncio.Lock with double-checked locking so only one fetch happens
per expiry window.

Closes #4677
2026-07-04 21:19:12 +08:00
5283ceae85 Add optional Nanobot plugin controls (#4396)
* feat: add optional nanobot features

* test: update azure install hint expectation

* fix: validate optional feature extras

maintainer edit: verify requested dependency extras before treating optional features as installed, propagate restart state from feature enablement, and align docs with the new plugins enable command.

* fix: bound optional feature installs

maintainer edit: make optional feature installs time out as a normal install failure instead of leaving the WebUI or CLI action waiting indefinitely.

* feat: slim optional channel dependencies

* fix: log optional install commands

* fix(webui): gate remote feature installs

* docs: clarify webhook plugin example

* fix(webui): harden optional feature installs

* fix: install optional deps without package fallback

* fix(cli): refine plugin feature controls

* fix(webui): count enabled nanobot features

* fix(webui): allow slow feature install routes

* fix(webui): allow disabling websocket channel

* fix(plugins): simplify optional feature controls

* fix(webui): polish apps catalog states

* fix(webui): confirm nanobot support installs

* fix(webui): polish nanobot install dialog

* fix(webui): suppress empty websocket handshakes

* fix(webui): clarify apps plugin summary

* fix(webui): localize workspace access copy

* fix(plugins): polish optional feature controls (#4691)

---------

Co-authored-by: Xubin Ren <52506698+Re-bin@users.noreply.github.com>
2026-07-03 18:17:52 +08:00
Hamb_yandGitHub 00cc0da530 fix(providers): omit temperature for sonnet 5
Add sonnet-5 to the Anthropic omit-temperature model families and cover adaptive, enabled, and non-thinking request paths.

Fixes #4683
2026-07-03 15:48:46 +08:00
chengyongruandXubin Ren 44a5ed1bc0 feat(providers): support provider-scoped proxy config 2026-06-30 17:33:36 +08:00
chengyongruandXubin Ren 593b328dbb test: cover Copilot enterprise overrides
Maintainer edit: add mocked coverage for the enterprise endpoint and client ID override paths, and document the environment variables users must set before OAuth login.
2026-06-30 15:21:19 +08:00
04cbandXubin Ren 4beca25ceb feat(providers): allow GitHub Copilot endpoint overrides for enterprise/GHE (#4220) 2026-06-30 15:21:19 +08:00
Xubin Ren 00a7de0171 fix: stringify Anthropic typeless blocks as JSON 2026-06-27 16:47:59 +08:00
axelray-devandXubin Ren d1ae73a8a8 fix: add clear error message for invalid thinking_style values
Widen thinking_style from Literal to str | None and add a
@field_validator that produces a helpful error message listing
valid options when an invalid value is provided.

Addresses the review feedback on #4482.
2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren c661012754 fix: coalesce None thinking_style to empty string in provider creation
ProviderConfig.thinking_style defaults to None (Optional field), but
create_dynamic_spec expects a string. Coalesce None to "" at all call
sites (factory.py, settings_api.py) and fix the test assertion to
expect None from the config default.
2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren ceae6d7b61 fix: allow custom provider to configure thinking style (#4429) 2026-06-25 22:53:15 +08:00
chengyongruandXubin Ren 4378944459 test: speed up test suite 2026-06-25 16:10:28 +08:00
Xubin Ren f9afc9389b fix(providers): apply Kimi Coding default headers 2026-06-24 10:43:16 +08:00
chengyongruandXubin Ren 6d989de336 docs: document OpenCode provider setup
Maintainer edit: document OpenCode Zen and Go configuration, keep their registry entries with gateway providers, and add focused provider registration tests.
2026-06-24 10:25:12 +08:00
chengyongruandXubin Ren 0e9861558a fix: keep duplicate id repair in Anthropic provider
maintainer edit: move duplicate tool_use history repair out of AgentRunner and into Anthropic message conversion, reusing the OpenAI-compatible queue-mapping approach locally without broadening the shared runner path.
2026-06-24 10:21:48 +08:00
chengyongruandXubin Ren 853aecdb97 fix: preserve duplicate-id tool calls
maintainer edit: remap duplicate tool_use/tool_call ids instead of dropping later calls, so Anthropic-compatible providers that reuse ids for distinct parallel tool calls keep all requested work while still sending unique ids.
2026-06-24 10:21:48 +08:00
sbyininandXubin Ren 6da56f3574 Fix OpenAI reference image home expansion test on Windows 2026-06-19 17:16:09 +08:00
sbyininandXubin Ren 44f7bbae50 fix openai image reference home paths 2026-06-19 17:16:09 +08:00
sbyininandXubin Ren adb737b614 fix openai image reference edits 2026-06-19 17:16:09 +08:00
Xubin Ren 58a14d18dd test: tighten image placeholder assertions 2026-06-19 14:59:52 +08:00
Xubin Ren de5e216583 test: fix image strip test formatting 2026-06-19 14:59:52 +08:00
michaelxerandXubin Ren 7917466f4e fix(tests): update image placeholder assertions for new non-descriptive text
The _strip_image_content methods now use a fixed non-descriptive
placeholder instead of path-derived text. Update the 3 existing
test_provider_retry assertions to match the new placeholder format.
2026-06-19 14:59:52 +08:00