Skip to content

feat(provider): add Dots chat completion provider - #10159

Closed
wcqqq1214 wants to merge 12 commits into
AstrBotDevs:masterfrom
wcqqq1214:feat/dots-provider-adapter
Closed

wcqqq1214 wants to merge 12 commits into
AstrBotDevs:masterfrom
wcqqq1214:feat/dots-provider-adapter

Conversation

@wcqqq1214

@wcqqq1214 wcqqq1214 commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Refs #10094.

Add an opt-in Dots provider that converts native <dots_function_call> blocks into standard tool calls, keeping protocol markup out of assistant replies.

This PR covers text, tool calls, and streaming. Dots-specific audio/video adaptation and validation are not included.

Modifications / 改动点

  • Add Dots configuration and per-request api-key authentication, preserving key rotation, bearer authentication, and proxy settings.

  • Support native XML/JSON calls and incremental streaming. Preserve argument whitespace, exclude reasoning examples from execution, and prefer standard calls over duplicate native blocks.

  • Add regression tests and English/Chinese setup and migration guides. Existing configurations are unchanged; migration is manual.

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

ruff format . && ruff check .
Passed

python -m pytest tests/test_dots_source.py tests/test_openai_source.py tests/test_tool_loop_agent_runner.py tests/test_openai_thinking_tags.py -q
393 passed

cd docs && npm run docs:build
Passed

The docs build used npm with existing dependencies because local pnpm blocked esbuild's install script. No dependencies changed.

WebUI verified: Providers → Chat Completion → Add → Dots, model configuration controls, and Dots selection under Config → AI → Model. Both guides match the current controls; no existing navigation or screenshots require replacement.

The original issue's full group-chat environment remains unverified. Cross-turn search deduplication, search-service rate limits, and model-generated off-topic replies are outside this PR's scope.


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 📚 I checked the affected WebUI instructions and screenshots in docs/zh and docs/en against the changed navigation, page structure, and labels, and updated them in this PR (or explained why no documentation update is needed). For renamed, moved, or merged entry points, I included an old entry → new entry mapping in the documentation and changelog.
    / 我已对照变化后的 WebUI 入口、页面结构和术语,核对并在本 PR 中更新 docs/zhdocs/en 的相关操作说明与截图(或说明无需更新文档的原因)。入口改名、移动或合并时,已在文档和 changelog 中补充 旧入口 → 新入口 对照。

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Add an opt-in Dots chat completion provider with native tool-call compatibility, safe streaming support, WebUI integration, documentation, and regression tests.

New Features:

  • Add an opt-in Dots chat completion provider with default configuration, model discovery, authentication, proxy, and key rotation support.
  • Convert Dots-native XML and JSON function-call blocks into validated standard tool calls for both regular and streaming responses.
  • Add Dots provider setup, migration, tool-calling, and streaming documentation in English and Chinese.
  • Add a Dots provider icon to the WebUI.

Bug Fixes:

  • Prevent native call markup and reasoning examples from appearing in assistant replies or triggering unintended tool execution.
  • Preserve argument whitespace and prioritize standard tool calls when native and standard calls are duplicated.

Enhancements:

  • Buffer ambiguous native markers during tool-enabled streaming while releasing safe assistant text promptly and preserving reasoning, usage, and failure handling.

Documentation:

  • Document Dots provider setup, migration from OpenAI Compatible configurations, supported tool-call behavior, and streaming limitations in both supported languages.

Tests:

  • Add comprehensive regression coverage for authentication rotation, XML/JSON parsing, schema validation, reasoning exclusion, standard-call precedence, streaming, and agent tool-loop behavior.

@wcqqq1214
wcqqq1214 marked this pull request as ready for review September 20, 2026 13:59

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/dots_source.py" line_range="76-79" />
<code_context>
+
+        Raises:
+            ValueError: The block, tool name, or arguments are invalid.
+        """
+        calls = []
+        if body.lstrip().startswith("{"):
+            try:
+                call = json.loads(body)
+            except json.JSONDecodeError as exc:
+                raise ValueError("Invalid Dots JSON tool call") from exc
+            calls.append(
+                (call.get("name"), call.get("arguments", call.get("parameters", {})))
+            )
+        else:
+            invoke_pattern = re.compile(
</code_context>
<issue_to_address>
**issue (bug_risk):** A syntactically valid JSON value that is not an object, such as `[]` or `null`, causes `call.get(...)` to raise `AttributeError` instead of the documented `ValueError`, so malformed native calls escape the provider's validation path.

**Triggers:** When a Dots JSON function-call body parses to a non-object JSON value.

**Suggested fix:** Check `isinstance(call, dict)` immediately after `json.loads` and raise `ValueError` for other JSON types.

```suggestion
            try:
                call = json.loads(body)
            except json.JSONDecodeError as exc:
                raise ValueError("Invalid Dots JSON tool call") from exc
            if not isinstance(call, dict):
                raise ValueError("Invalid Dots JSON tool call")
```
</issue_to_address>

### Comment 2
<location path="astrbot/core/provider/sources/dots_source.py" line_range="207-210" />
<code_context>
+            normalized = completion.model_dump()
+            normalized["choices"][0]["message"]["content"] = (
+                re.sub(
+                    r"<dots_function_call>.*?</dots_function_call>"
+                    r"|<dots_function_call.*\Z|</dots_function_call>",
+                    "",
+                    content,
+                    flags=re.DOTALL,
+                ).strip()
</code_context>
<issue_to_address>
**issue (bug_risk):** When a standard tool call is present alongside an incomplete `<dots_function_call` marker, the fallback regex removes the marker and every subsequent character, including legitimate assistant text after the marker; standard-call precedence therefore silently truncates the reply.

**Triggers:** When a response contains an authoritative standard tool call, a malformed native-call opening marker, and ordinary text after that marker.

**Suggested fix:** Remove only the native markup span, or preserve text after an incomplete marker rather than applying a `.*\Z` suffix deletion.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and this adds a new external API integration that transmits provider keys and can turn model-generated Dots markup into executable tool calls, so a parsing or endpoint mistake could expose credentials or trigger external actions before a revert. Reverting stops future requests, but credentials already sent and tool effects that already occurred cannot be undone.

Blocking findings: astrbot/core/provider/sources/dots_source.py:79, astrbot/core/provider/sources/dots_source.py:210


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +76 to +79
try:
call = json.loads(body)
except json.JSONDecodeError as exc:
raise ValueError("Invalid Dots JSON tool call") from exc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): A syntactically valid JSON value that is not an object, such as [] or null, causes call.get(...) to raise AttributeError instead of the documented ValueError, so malformed native calls escape the provider's validation path.

Triggers: When a Dots JSON function-call body parses to a non-object JSON value.

Suggested fix: Check isinstance(call, dict) immediately after json.loads and raise ValueError for other JSON types.

Suggested change
try:
call = json.loads(body)
except json.JSONDecodeError as exc:
raise ValueError("Invalid Dots JSON tool call") from exc
try:
call = json.loads(body)
except json.JSONDecodeError as exc:
raise ValueError("Invalid Dots JSON tool call") from exc
if not isinstance(call, dict):
raise ValueError("Invalid Dots JSON tool call")

Comment on lines +207 to +210
r"<dots_function_call>.*?</dots_function_call>"
r"|<dots_function_call.*\Z|</dots_function_call>",
"",
content,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): When a standard tool call is present alongside an incomplete <dots_function_call marker, the fallback regex removes the marker and every subsequent character, including legitimate assistant text after the marker; standard-call precedence therefore silently truncates the reply.

Triggers: When a response contains an authoritative standard tool call, a malformed native-call opening marker, and ordinary text after that marker.

Suggested fix: Remove only the native markup span, or preserve text after an incomplete marker rather than applying a .*\Z suffix deletion.

@wcqqq1214

Copy link
Copy Markdown
Contributor Author

@codex review

@Soulter Soulter closed this Sep 21, 2026
@wcqqq1214
wcqqq1214 deleted the feat/dots-provider-adapter branch September 21, 2026 08:50
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.

2 participants