Skip to content

feat: 添加通用 Rerank 适配器#5816

Open
piexian wants to merge 3 commits intoAstrBotDevs:masterfrom
piexian:feat/generic-rerank
Open

feat: 添加通用 Rerank 适配器#5816
piexian wants to merge 3 commits intoAstrBotDevs:masterfrom
piexian:feat/generic-rerank

Conversation

@piexian
Copy link
Contributor

@piexian piexian commented Mar 6, 2026

新增一个面向 NewAPI 兼容 Rerank 接口的通用重排序供应商适配器,用于解决当前缺少可复用通用适配器的问题。

主要解决以下场景:

  • 用户需要直接填写完整的重排序请求地址
  • 使用 Bearer Token 进行认证
  • 请求体与返回结果兼容 NewAPI 风格

同时修正了配置文案,明确说明这里需要填写完整请求 URL,AstrBot 不会自动在末尾补全 /v1/rerank

Modifications / 改动点

  • astrbot/core/provider/sources/openai_rerank_source.py 中新增通用 Rerank 适配器实现

  • astrbot/core/provider/manager.py 中注册新的 Rerank 供应商

  • astrbot/core/config/default.py 中补充默认配置模板,并更新重排序相关配置提示文案

  • 更新前端i18n文案:

    • dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
    • dashboard/src/i18n/locales/en-US/features/config-metadata.json
  • tests/test_openai_rerank_source.py 中补充定向测试

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

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

验证步骤:

启动填写过后点击测试

测试结果:

image image image image

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ 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.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

添加一个通用的、兼容 NewAPI 的重排(rerank)提供方适配器,并将其接入到 provider 配置与管理器中。

New Features:

  • 引入一个通用的 OpenAI 风格重排提供方适配器,支持 Bearer 鉴权以及可配置的 endpoint/model。

Enhancements:

  • 更新默认 provider 模板以使用通用重排提供方,并为完整的重排 API URL 添加显式配置。
  • 在 provider 管理器中注册新的重排提供方类型,并优化前端关于重排设置的配置提示。

Documentation:

  • 在控制台的 i18n 元数据中澄清重排配置的说明,包括必须提供完整请求 URL 的要求。

Tests:

  • 为新的重排提供方添加单元测试,覆盖初始化、payload 构造、文档校验以及客户端生命周期等内容。
Original summary in English

Summary by Sourcery

Add a generic NewAPI-compatible rerank provider adapter and wire it into the provider configuration and manager.

New Features:

  • Introduce a generic OpenAI-style rerank provider adapter supporting bearer auth and configurable endpoint/model.

Enhancements:

  • Update default provider templates to use the generic rerank provider and add explicit configuration for full rerank API URLs.
  • Register the new rerank provider type in the provider manager and refine frontend config hints for rerank settings.

Documentation:

  • Clarify rerank configuration descriptions in the dashboard i18n metadata, including the requirement to provide a full request URL.

Tests:

  • Add unit tests covering initialization, payload construction, document validation, and client lifecycle for the new rerank provider.
Original summary in English

Summary by Sourcery

添加一个通用的、兼容 NewAPI 的重排(rerank)提供方适配器,并将其接入到 provider 配置与管理器中。

New Features:

  • 引入一个通用的 OpenAI 风格重排提供方适配器,支持 Bearer 鉴权以及可配置的 endpoint/model。

Enhancements:

  • 更新默认 provider 模板以使用通用重排提供方,并为完整的重排 API URL 添加显式配置。
  • 在 provider 管理器中注册新的重排提供方类型,并优化前端关于重排设置的配置提示。

Documentation:

  • 在控制台的 i18n 元数据中澄清重排配置的说明,包括必须提供完整请求 URL 的要求。

Tests:

  • 为新的重排提供方添加单元测试,覆盖初始化、payload 构造、文档校验以及客户端生命周期等内容。
Original summary in English

Summary by Sourcery

Add a generic NewAPI-compatible rerank provider adapter and wire it into the provider configuration and manager.

New Features:

  • Introduce a generic OpenAI-style rerank provider adapter supporting bearer auth and configurable endpoint/model.

Enhancements:

  • Update default provider templates to use the generic rerank provider and add explicit configuration for full rerank API URLs.
  • Register the new rerank provider type in the provider manager and refine frontend config hints for rerank settings.

Documentation:

  • Clarify rerank configuration descriptions in the dashboard i18n metadata, including the requirement to provide a full request URL.

Tests:

  • Add unit tests covering initialization, payload construction, document validation, and client lifecycle for the new rerank provider.

@auto-assign auto-assign bot requested review from LIghtJUNction and anka-afk March 6, 2026 23:16
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 6, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在通过引入一个通用的 Rerank 适配器来增强系统的灵活性和可扩展性。该适配器能够与各种兼容 NewAPI 的 Rerank 服务无缝集成,简化了用户配置和认证流程。同时,对配置文案的优化提升了用户体验,确保了清晰准确的指引。

Highlights

  • 新增通用 Rerank 适配器: 引入了一个新的通用 Rerank 供应商适配器,兼容 NewAPI 风格的 Rerank 接口,支持直接填写完整请求地址和 Bearer Token 认证。
  • 配置更新: 在默认配置中添加了新的通用 Rerank 配置模板,并修正了现有 Rerank 配置的提示文案,明确指出需要填写完整的请求 URL。
  • 国际化支持: 更新了前端仪表盘的中文和英文国际化文件,以支持新的配置项和提示信息。
  • 测试覆盖: 为新的通用 Rerank 适配器添加了专门的单元测试,确保其功能正确性和稳定性。
Changelog
  • astrbot/core/config/default.py
    • 更新了现有配置模板中的 'provider' 字段,将其从 'openai' 更改为 'generic'。
    • 新增了一个名为 '通用 Rerank' 的配置模板,用于支持新的通用 Rerank 适配器。
    • 添加了 'rerank_api_url' 配置字段,并提供了详细的提示信息,说明其仅适用于通用 Rerank 适配器且需填写完整 URL。
  • astrbot/core/provider/manager.py
    • 在动态导入逻辑中添加了对 'openai_rerank' 类型的支持,以注册新的 OpenAIRerankProvider。
  • astrbot/core/provider/sources/openai_rerank_source.py
    • 新增了 OpenAIRerankProvider 类,实现了通用的 Rerank 适配器逻辑,包括请求构建、响应解析和错误处理。
  • dashboard/src/i18n/locales/en-US/features/config-metadata.json
    • 添加了 'rerank_api_url' 的英文描述和提示信息,以支持前端界面的国际化显示。
  • dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
    • 添加了 'rerank_api_url' 的中文描述和提示信息,以支持前端界面的国际化显示。
  • tests/test_openai_rerank_source.py
    • 新增了针对 OpenAIRerankProvider 的单元测试,验证了适配器的初始化、Rerank 请求的构建和响应处理,以及对不同文档类型的支持。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. label Mar 6, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 4 个问题,并给出了一些整体性的反馈:

  • default.py 中,新增加的「通用 Rerank」模板使用的是 "provider": "openai",而 OpenAI 的聊天补全模板已经切换为 "generic";建议这里也统一 provider 标识,以避免把这个通用适配器和特定于 OpenAI 的适配器混淆。
  • OpenAIRerankProvider.rerank 方法目前的类型标注是 documents: list[str],但 _normalize_documents 和测试都同时支持字符串和映射;建议将类型注解以及相关接口更新为 list[str | Mapping] 之类的形式,使实际接受的输入更加清晰,并避免被误用。
给 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
-`default.py` 中,新增加的「通用 Rerank」模板使用的是 `"provider": "openai"`,而 OpenAI 的聊天补全模板已经切换为 `"generic"`;建议这里也统一 provider 标识,以避免把这个通用适配器和特定于 OpenAI 的适配器混淆。
- `OpenAIRerankProvider.rerank` 方法目前的类型标注是 `documents: list[str]`,但 `_normalize_documents` 和测试都同时支持字符串和映射;建议将类型注解以及相关接口更新为 `list[str | Mapping]` 之类的形式,使实际接受的输入更加清晰,并避免被误用。

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="138-147" />
<code_context>
+    async def rerank(
+        self,
+        query: str,
+        documents: list[str],
+        top_n: int | None = None,
+    ) -> list[RerankResult]:
+        if not documents:
+            return []
+        if not query.strip():
+            logger.warning("通用 Rerank 查询文本为空,返回空结果。")
+            return []
+        if not self.client:
+            logger.error("通用 Rerank 客户端会话已关闭,返回空结果。")
+            return []
+
+        payload = self._build_payload(
+            query=query,
+            documents=self._normalize_documents(documents),
+            top_n=top_n,
+        )
</code_context>
<issue_to_address>
**suggestion:** `rerank``documents` 的类型注解比 `_normalize_documents` 所支持的范围更窄。

由于 `_normalize_documents` 同时接受字符串和映射类型,并且会对不支持的类型抛出异常,目前的 `list[str]` 注解并没有反映真实可接受的输入范围,也可能误导类型检查器和 IDE。建议放宽类型(例如 `list[str | Mapping[str, Any]]``list[Any]`),以与真实契约保持一致。

Suggested implementation:

```python
    async def rerank(
        self,
        query: str,
        documents: list[str | Mapping[str, Any]],
        top_n: int | None = None,
    ) -> list[RerankResult]:

```

1. 确保在 `openai_rerank_source.py` 文件开头导入了 `Mapping``Any`,通常为:
   `from typing import Any, Mapping`
2. 如果代码库中使用了 `typing` 类型别名(例如 `DocumentInput = str | Mapping[str, Any]`),可以考虑定义并使用这样的别名,而不是内联联合类型,以便在不同 provider 之间保持类型一致。
</issue_to_address>

### Comment 2
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="42-47" />
<code_context>
+        if not self.api_key:
+            raise ValueError("通用 Rerank API Key 不能为空。")
+
+        self.client = aiohttp.ClientSession(
+            headers={
+                "Authorization": f"Bearer {self.api_key}",
+                "Content-Type": "application/json",
+            },
+            timeout=aiohttp.ClientTimeout(total=self.timeout),
+        )
+
</code_context>
<issue_to_address>
**issue (bug_risk):**`__init__` 中创建 `aiohttp.ClientSession` 可能存在问题,这取决于 provider 的构造方式。

这种做法假设 `__init__` 总是在一个已激活的事件循环中执行;但如果 provider 在导入时或事件循环启动之前就被构造,就可能导致 `DeprecationWarning`/`RuntimeWarning` 以及生命周期管理问题。建议在首次使用时再延迟创建 `ClientSession`(例如在 `rerank` 中或单独的异步初始化方法里),这样可以保证它一定是在运行中的事件循环内创建;或者需要在文档中明确说明并验证:外围框架只会在事件循环内构造此类实例。
</issue_to_address>

### Comment 3
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="159-161" />
<code_context>
+        try:
+            async with self.client.post(self.api_url, json=payload) as response:
+                if response.status >= 400:
+                    response_text = await response.text()
+                    raise RuntimeError(
+                        f"通用 Rerank API 请求失败: HTTP {response.status}, {response_text}"
+                    )
+
</code_context>
<issue_to_address>
**🚨 suggestion (security):** 在错误信息中包含完整响应体可能过大或包含敏感数据。

当前在 HTTP 出错时将完整响应体插入异常信息。对于通用的 rerank 接口,响应体可能非常大,并且可能包含用户内容。为减少日志量并避免泄露敏感数据,建议将 `response_text` 截断至固定长度,或用经过脱敏的摘要代替,同时保留状态码以及一小段片段即可。

Suggested implementation:

```python
        try:
            async with self.client.post(self.api_url, json=payload) as response:
                if response.status >= 400:
                    response_text = await response.text()
                    snippet = response_text[:200]
                    if len(response_text) > 200:
                        snippet += "...[truncated]"
                    logger.warning(
                        "通用 Rerank API 请求失败: HTTP %s, body snippet: %s",
                        response.status,
                        snippet,
                    )
                    raise RuntimeError(
                        f"通用 Rerank API 请求失败: HTTP {response.status}"
                    )

```

如果代码的其它部分依赖异常信息中携带完整响应体,可以考虑:
1. 调整相关异常处理逻辑,不再依赖错误文本中的完整响应内容。
2. 根据你的日志策略,酌情修改片段长度(此处为 200 字符)或日志级别(例如改用 `logger.debug` 而不是 `warning`)。
</issue_to_address>

### Comment 4
<location path="tests/test_openai_rerank_source.py" line_range="25" />
<code_context>
+        return self._text
+
+
+class _MockClient:
+    def __init__(self, response: _MockResponse):
+        self.response = response
</code_context>
<issue_to_address>
**suggestion (testing):** 建议增加一个小测试,用来断言 `terminate` 确实会关闭 client 并将其置空。

`terminate()` 已经在异步测试中被调用,但目前并未断言它的副作用。请增加一个聚焦的异步测试:给 provider 设置一个 mock client,调用 `terminate()`,然后断言 client 的 `close()` 被调用(例如通过一个 `closed` 标志位),并且 `provider.client` 被设置为 `None`,这样一旦清理逻辑发生变更,就能通过测试及时发现。

Suggested implementation:

```python
class _MockClient:
    def __init__(self, response: _MockResponse):
        self.response = response
        self.calls: list[tuple[str, dict]] = []
        self.closed = False

    def post(self, url: str, json: dict):
        self.calls.append((url, json))
        return self.response

    async def close(self):
        self.closed = True


@pytest.mark.asyncio
async def test_terminate_closes_and_nulls_client(provider):
    """Ensure terminate() closes the client and clears the reference."""
    mock_response = _MockResponse(payload={}, text="")
    mock_client = _MockClient(mock_response)
    provider.client = mock_client

    await provider.terminate()

    assert mock_client.closed is True
    assert provider.client is None

```

为使上述测试能够编译并通过,你可能需要:
1. 确保在 `tests/test_openai_rerank_source.py` 顶部导入了 `pytest`- `import pytest`
2.`test_terminate_closes_and_nulls_client``provider` 参数调整为与你现有的 fixture 或工厂保持一致:
   - 如果你当前使用了不同名称的 fixture(例如 `openai_rerank_source`),请相应地重命名该参数。
3. 确认 provider 对象具有 `client` 属性,并提供一个异步的 `terminate()` 方法,该方法会清理 `client` 并调用其 `close()`。
</issue_to_address>

Sourcery 对开源项目永久免费——如果你觉得这次的评审有帮助,欢迎分享 ✨
请帮我变得更有用!你可以在每条评论上点 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English

Hey - I've found 4 issues, and left some high level feedback:

  • In default.py, the new "通用 Rerank" template uses "provider": "openai" while the chat completion template for OpenAI was switched to "generic"; consider aligning the provider identifier here as well to avoid confusing this generic adapter with the OpenAI-specific one.
  • The OpenAIRerankProvider.rerank method is typed as documents: list[str] but _normalize_documents and the tests expect both strings and mappings; updating the type hints and any related interfaces to reflect list[str | Mapping] would make the accepted input clearer and prevent misuse.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `default.py`, the new "通用 Rerank" template uses `"provider": "openai"` while the chat completion template for OpenAI was switched to `"generic"`; consider aligning the provider identifier here as well to avoid confusing this generic adapter with the OpenAI-specific one.
- The `OpenAIRerankProvider.rerank` method is typed as `documents: list[str]` but `_normalize_documents` and the tests expect both strings and mappings; updating the type hints and any related interfaces to reflect `list[str | Mapping]` would make the accepted input clearer and prevent misuse.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="138-147" />
<code_context>
+    async def rerank(
+        self,
+        query: str,
+        documents: list[str],
+        top_n: int | None = None,
+    ) -> list[RerankResult]:
+        if not documents:
+            return []
+        if not query.strip():
+            logger.warning("通用 Rerank 查询文本为空,返回空结果。")
+            return []
+        if not self.client:
+            logger.error("通用 Rerank 客户端会话已关闭,返回空结果。")
+            return []
+
+        payload = self._build_payload(
+            query=query,
+            documents=self._normalize_documents(documents),
+            top_n=top_n,
+        )
</code_context>
<issue_to_address>
**suggestion:** The `documents` type annotation in `rerank` is narrower than what `_normalize_documents` supports.

Since `_normalize_documents` accepts both strings and mappings and will raise on unsupported types, the current `list[str]` annotation does not reflect the actual accepted inputs and can mislead type checkers and IDEs. Please widen the type (e.g., `list[str | Mapping[str, Any]]` or `list[Any]`) to match the real contract.

Suggested implementation:

```python
    async def rerank(
        self,
        query: str,
        documents: list[str | Mapping[str, Any]],
        top_n: int | None = None,
    ) -> list[RerankResult]:

```

1. Ensure `Mapping` and `Any` are imported at the top of `openai_rerank_source.py`, typically:
   `from typing import Any, Mapping`
2. If the codebase uses `typing` aliases (e.g. `DocumentInput = str | Mapping[str, Any]`), consider defining and using such an alias instead of an inline union, to keep types consistent across providers.
</issue_to_address>

### Comment 2
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="42-47" />
<code_context>
+        if not self.api_key:
+            raise ValueError("通用 Rerank API Key 不能为空。")
+
+        self.client = aiohttp.ClientSession(
+            headers={
+                "Authorization": f"Bearer {self.api_key}",
+                "Content-Type": "application/json",
+            },
+            timeout=aiohttp.ClientTimeout(total=self.timeout),
+        )
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Creating an `aiohttp.ClientSession` in `__init__` may be problematic depending on how providers are constructed.

This assumes `__init__` always runs inside an active event loop, which may not hold if providers are constructed at import time or before the loop starts, leading to `DeprecationWarning`/`RuntimeWarning` and lifecycle issues. Consider lazily creating the `ClientSession` on first use (e.g., in `rerank` or an async init method) so it’s guaranteed to be created within a running loop, or explicitly document and verify that the surrounding framework only constructs this class inside an event loop.
</issue_to_address>

### Comment 3
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="159-161" />
<code_context>
+        try:
+            async with self.client.post(self.api_url, json=payload) as response:
+                if response.status >= 400:
+                    response_text = await response.text()
+                    raise RuntimeError(
+                        f"通用 Rerank API 请求失败: HTTP {response.status}, {response_text}"
+                    )
+
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Including the full response body in error messages could be large or contain sensitive data.

You’re interpolating the full response body into the exception message on HTTP errors. For generic rerank endpoints this can be very large and may include user content. To reduce log volume and avoid leaking sensitive data, consider truncating `response_text` to a fixed length or replacing it with a sanitized summary while still including the status code and possibly a short snippet.

Suggested implementation:

```python
        try:
            async with self.client.post(self.api_url, json=payload) as response:
                if response.status >= 400:
                    response_text = await response.text()
                    snippet = response_text[:200]
                    if len(response_text) > 200:
                        snippet += "...[truncated]"
                    logger.warning(
                        "通用 Rerank API 请求失败: HTTP %s, body snippet: %s",
                        response.status,
                        snippet,
                    )
                    raise RuntimeError(
                        f"通用 Rerank API 请求失败: HTTP {response.status}"
                    )

```

If other parts of this method rely on the exception message containing the full response body, you may want to:
1. Adjust any exception handling to no longer expect the full body in the error text.
2. Optionally tune the snippet length (200 chars here) or the logging level (e.g., `logger.debug` instead of `warning`) according to your logging policy.
</issue_to_address>

### Comment 4
<location path="tests/test_openai_rerank_source.py" line_range="25" />
<code_context>
+        return self._text
+
+
+class _MockClient:
+    def __init__(self, response: _MockResponse):
+        self.response = response
</code_context>
<issue_to_address>
**suggestion (testing):** Add a small test to assert `terminate` actually closes and nulls the client

`terminate()` is already used in async tests, but its side effects aren’t asserted. Please add a focused async test that sets a mock client on the provider, calls `terminate()`, and then asserts the client’s `close()` was called (e.g., via a `closed` flag) and `provider.client` is set to `None`, so changes to the cleanup behavior are caught by tests.

Suggested implementation:

```python
class _MockClient:
    def __init__(self, response: _MockResponse):
        self.response = response
        self.calls: list[tuple[str, dict]] = []
        self.closed = False

    def post(self, url: str, json: dict):
        self.calls.append((url, json))
        return self.response

    async def close(self):
        self.closed = True


@pytest.mark.asyncio
async def test_terminate_closes_and_nulls_client(provider):
    """Ensure terminate() closes the client and clears the reference."""
    mock_response = _MockResponse(payload={}, text="")
    mock_client = _MockClient(mock_response)
    provider.client = mock_client

    await provider.terminate()

    assert mock_client.closed is True
    assert provider.client is None

```

To make this compile and pass, you may need to:
1. Ensure `pytest` is imported at the top of `tests/test_openai_rerank_source.py`:
   - `import pytest`
2. Adjust the `provider` argument in `test_terminate_closes_and_nulls_client` to match your existing fixture or factory:
   - If you currently use a differently named fixture (e.g. `openai_rerank_source`), rename the parameter accordingly.
3. Confirm that the provider object has a `client` attribute and an async `terminate()` method that clears `client` and calls `close()` on it.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

本次 PR 新增了一个通用的 Rerank 适配器,用于兼容 NewAPI 风格的接口,并更新了相关配置和文档。代码实现质量很高,并包含了完整的单元测试。

我提出了两点改进建议:

  1. 为了更好地体现适配器的通用性,建议修改适配器的相关命名(如类名、文件名、类型名),使其不局限于 'openai'。
  2. 建议将代码中的魔法数 100 定义为常量,以提高代码的可读性和可维护性。

@zouyonghe
Copy link
Member

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题,并给出了一些总体性的反馈:

  • _build_payload 中硬编码的 top_k 上限 100 可能与所有后端不匹配;建议把这个限制做成可配置项(或者从 provider_settings 中推导),而不是内嵌一个 magic number。
  • OpenAIRerankProvider.__init__ 中,timeout 被直接强制转换为 int;更安全的做法是先校验它是否为正数,如果传入的值无效,则回退到一个默认值或抛出更清晰的错误。
给 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- The hardcoded `top_k` cap of 100 in `_build_payload` may not match all backends; consider making this limit configurable (or derived from provider_settings) instead of embedding a magic number.
- In `OpenAIRerankProvider.__init__`, `timeout` is blindly cast to `int`; it might be safer to validate that it is a positive number and fall back to a default or raise a clearer error if the provided value is invalid.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="94-103" />
<code_context>
+            async with client.post(self.api_url, json=payload) as response:
+                if response.status >= 400:
+                    response_text = await response.text()
+                    logger.warning(
+                        "通用 Rerank API 请求失败: HTTP %s, body snippet: %s",
+                        response.status,
+                        self._truncate_error_response(response_text),
+                    )
+                    raise RuntimeError(
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Consider whether logging raw response snippets may leak sensitive data from the rerank provider.

The warning log currently includes up to 200 chars of the raw response body, which may contain query text, document content, or other sensitive data depending on the provider’s error format. Consider either redacting the body, restricting logs to safe structured fields (e.g., JSON error code/message), or moving the raw snippet to a debug-level log instead of warning.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享给更多人 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The hardcoded top_k cap of 100 in _build_payload may not match all backends; consider making this limit configurable (or derived from provider_settings) instead of embedding a magic number.
  • In OpenAIRerankProvider.__init__, timeout is blindly cast to int; it might be safer to validate that it is a positive number and fall back to a default or raise a clearer error if the provided value is invalid.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The hardcoded `top_k` cap of 100 in `_build_payload` may not match all backends; consider making this limit configurable (or derived from provider_settings) instead of embedding a magic number.
- In `OpenAIRerankProvider.__init__`, `timeout` is blindly cast to `int`; it might be safer to validate that it is a positive number and fall back to a default or raise a clearer error if the provided value is invalid.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/openai_rerank_source.py" line_range="94-103" />
<code_context>
+            async with client.post(self.api_url, json=payload) as response:
+                if response.status >= 400:
+                    response_text = await response.text()
+                    logger.warning(
+                        "通用 Rerank API 请求失败: HTTP %s, body snippet: %s",
+                        response.status,
+                        self._truncate_error_response(response_text),
+                    )
+                    raise RuntimeError(
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Consider whether logging raw response snippets may leak sensitive data from the rerank provider.

The warning log currently includes up to 200 chars of the raw response body, which may contain query text, document content, or other sensitive data depending on the provider’s error format. Consider either redacting the body, restricting logs to safe structured fields (e.g., JSON error code/message), or moving the raw snippet to a debug-level log instead of warning.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +94 to +103
logger.warning(
f"通用 Rerank top_n={top_n} 超出接口限制,已截断为 {top_k}。"
)
payload["top_k"] = top_k

return payload

@staticmethod
def _parse_results(response_data: Any) -> list[RerankResult]:
if not isinstance(response_data, dict):
Copy link
Contributor

Choose a reason for hiding this comment

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

🚨 suggestion (security): 请考虑记录原始响应片段时,是否可能从 rerank 提供方泄露敏感数据。

当前的 warning 日志会包含最长 200 个字符的原始响应 body,根据提供方的错误格式,这里面可能包含查询文本、文档内容或其他敏感数据。建议要么对 body 做脱敏处理,要么只记录安全的结构化字段(例如 JSON 错误码 / 错误信息),或者将原始片段日志下沉到 debug 级别,而不是 warning。

Original comment in English

🚨 suggestion (security): Consider whether logging raw response snippets may leak sensitive data from the rerank provider.

The warning log currently includes up to 200 chars of the raw response body, which may contain query text, document content, or other sensitive data depending on the provider’s error format. Consider either redacting the body, restricting logs to safe structured fields (e.g., JSON error code/message), or moving the raw snippet to a debug-level log instead of warning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants