Skip to content

Upstream sync — litellm → openai_compat_provider, timezone, cron improvements#14

Merged
kmbandy merged 45 commits into
mainfrom
feature-forksync-v5
Mar 27, 2026
Merged

Upstream sync — litellm → openai_compat_provider, timezone, cron improvements#14
kmbandy merged 45 commits into
mainfrom
feature-forksync-v5

Conversation

@kmbandy
Copy link
Copy Markdown
Owner

@kmbandy kmbandy commented Mar 27, 2026

Summary

Weekly upstream sync from HKUDS/nanobot. Resolves all 7 conflict files, migrates our custom patches from the deleted litellm_provider.py to the new openai_compat_provider.py.

Upstream changes pulled in

  • litellm → native SDKs: litellm_provider.py removed upstream, replaced with openai_compat_provider.py + native Anthropic/Azure/Codex providers
  • Timezone support: agent-level IANA timezone config, propagated into runtime context and cron scheduling
  • Cron improvements: workspace-scoped cron store, timezone-aware scheduling, cron state scoped per workspace
  • Provider fixes: Step Fun provider, OpenAI o1 max_completion_tokens, Gemini thought signature round-trip, plain-text response handling
  • Channel improvements: Telegram streaming fixes, message send retry with exponential backoff, QQ image/file support, Feishu thread replies, WhatsApp group_policy
  • WeiXin: QR code auto-refresh, session persistence, polling fixes

Our patches preserved

  • suppress_tools_param — skips tools param for models that reject it (Qwen3.5-4B). Moved from litellm_provider.pyOpenAICompatProvider
  • request_timeout — configurable per-provider timeout. Same migration.
  • _extract_text_tool_calls — parses tool calls embedded in text content (Qwen3 <tool_call>, Nemotron [TOOL_CALLS], XML, Python code-block, raw JSON). Added to both dict and SDK object parse paths in OpenAICompatProvider.
  • EOS token stripping — removes <|im_end|>, <|endoftext|>, </s>, <|eot_id|> from response content.
  • sysmon tool wiring in commands.py and loop.py.

Conflicts resolved

  • providers/registry.py — removed litellm_prefix/skip_prefixes, accepted backend="openai_compat"
  • config/schema.py — kept http: HttpConfig, added upstream send_max_retries
  • agent/context.py — merged timezone param alongside our memory compaction params
  • agent/loop.py — merged timezone + sysmon params, updated ContextBuilder instantiation
  • cli/commands.py — moved suppress_tools_param/request_timeout to OpenAICompatProvider branch; kept sysmon + timezone wiring
  • agent/tools/cron.py — kept our relative time parsing, merged upstream timezone-aware at handling
  • providers/litellm_provider.py — accepted deletion via git rm

MiguelPF and others added 30 commits March 18, 2026 10:11
Replace `get_cron_dir()` with `config.workspace_path / "cron"` so each
workspace keeps its own `jobs.json`.  This lets users run multiple
nanobot instances with independent cron schedules without cross-talk.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When upgrading, if jobs.json exists at the old global path and not yet
at the workspace path, move it automatically.  Prevents silent loss of
existing cron jobs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement file upload and sending for QQ C2C messages

Reference: https://github.com/tencent-connect/botpy/blob/master/examples/demo_c2c_reply_file.py

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: chengyongru <chengyongru.ai@gmail.com>
- Fix _read_media_bytes treating local paths as URLs: local file
  handling code was dead code placed after an early return inside the
  HTTP try/except block. Restructure to check for local paths (plain
  path or file:// URI) before URL validation, so files like
  /home/.../.nanobot/workspace/generated_image.svg can be read and
  sent correctly.
- Add .svg to _IMAGE_EXTS so SVG files are uploaded as file_type=1
  (image) instead of file_type=4 (file).
- Add tests for local path, file:// URI, and missing file cases.

Fixes: HKUDS#1667 (comment)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
urlparse on Windows puts the path in netloc, not path. Use
(parsed.path or parsed.netloc) to get the correct raw path.
Keep cron state workspace-scoped while only migrating legacy jobs into the default workspace. This preserves seamless upgrades for existing installs without polluting intentionally new workspaces.
…th safe default-only migration

fix(cron): scope cron state to each workspace with safe default-only migration
Keep the channel enhancements aligned with the current codebase while preserving a simpler product surface. This keeps QQ, Feishu, Telegram, and WhatsApp improvements together, removes the extra Telegram-only tool hint toggle, and makes WhatsApp mention-only groups actually work.
… WhatsApp

feat: telegram/qq/whatsapp/feishu enhancement
Keep the mypy-friendly optional execute signatures while returning clearer errors for missing arguments and locking that behavior with regression tests.

Made-with: Cursor
- Remove litellm dependency entirely (supply chain risk mitigation)
- Add AnthropicProvider (native SDK) and OpenAICompatProvider (unified)
- Merge CustomProvider into OpenAICompatProvider, delete custom_provider.py
- Add ProviderSpec.backend field for declarative provider routing
- Remove _resolve_model, find_gateway, find_by_model (dead heuristics)
- Pass resolved spec directly into provider — zero internal lookups
- Stub out litellm-dependent model database (cli/models.py)
- Add anthropic>=0.45.0 to dependencies, remove litellm
- 593 tests passed, net -1034 lines
- Prevent repeated retries on expired sessions in the polling thread
- Stop sending messages to invalid agent sessions to eliminate noise logs and unnecessary requests
Clarify group policy behavior for bot responses in group channels.
Seeratul and others added 15 commits March 25, 2026 09:08
Clarified instructions for group policy behavior in README.
Handle string and dict-shaped responses from OpenAI-compatible backends so non-standard providers no longer crash on missing choices fields. Add regression tests to keep SDK, dict, and plain-text parsing paths aligned.
…t_signature round-trip

Replace the flatten/unflatten approach (merging extra_content.google.*
into provider_specific_fields then reconstructing) with direct pass-through:
parse extra_content as-is, store on ToolCallRequest.extra_content, serialize
back untouched.  This is lossless, requires no hardcoded field names, and
covers all three parsing branches (str, dict, SDK object) plus streaming.
Add agent-level timezone configuration with a UTC default, propagate it into runtime context and heartbeat prompts, and document valid IANA timezone usage in the README.
Make cron use the configured agent timezone when a cron expression omits tz or a one-shot ISO time has no offset. This keeps runtime context, heartbeat, and scheduling aligned around the same notion of time.

Made-with: Cursor
Make cron list output render one-shot and run-state timestamps in the same timezone context used to interpret schedules. This keeps scheduling logic and user-facing time displays consistent.

Made-with: Cursor
Read the default timezone from the agent context when wiring the cron tool so startup no longer depends on an out-of-scope local variable. Add a regression test to ensure AgentLoop passes the configured timezone through to cron.

Made-with: Cursor
- Add send_max_retries config option (default: 3, range: 0-10)
- Implement _send_with_retry in ChannelManager with 1s/2s/4s backoff
- Propagate CancelledError for graceful shutdown
- Fix telegram send_delta to raise exceptions for Manager retry
- Add comprehensive tests for retry logic
- Document channel settings in README
Make channel delivery failures raise consistently so retry policy lives in ChannelManager rather than being split across individual channels. Tighten Telegram stream finalization, clarify sendMaxRetries semantics, and align the docs with the behavior the system actually guarantees.
Key changes from upstream:
- Replace litellm_provider.py with openai_compat_provider.py (native openai SDK)
- Add backend-based provider routing (openai_compat / anthropic / azure_openai / codex)
- Add timezone support to ContextBuilder and AgentLoop
- Add send_max_retries to ChannelConfig
- Update registry: remove litellm_prefix/skip_prefixes, add backend field

Preserved custom patches:
- suppress_tools_param + request_timeout on OpenAICompatProvider
- _extract_text_tool_calls (Qwen3/Nemotron/XML/JSON text-embedded tool call parsing)
- EOS token stripping (<|im_end|>, </s>, etc.)
- HttpConfig field on ChannelConfig
- memory_max_chars/tokens/compaction_enabled on ContextBuilder + AgentLoop
- sysmon param on AgentLoop
- _parse_at relative time parsing in CronTool

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kmbandy kmbandy merged commit 1a153db into main Mar 27, 2026
0 of 3 checks passed
@kmbandy kmbandy deleted the feature-forksync-v5 branch March 27, 2026 00:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.