Skip to content

Feature(provider): add i18n registration path for provider adapters#6889

Closed
Li-shi-ling wants to merge 6 commits intoAstrBotDevs:masterfrom
Li-shi-ling:feature/i18n-injector-for-providers
Closed

Feature(provider): add i18n registration path for provider adapters#6889
Li-shi-ling wants to merge 6 commits intoAstrBotDevs:masterfrom
Li-shi-ling:feature/i18n-injector-for-providers

Conversation

@Li-shi-ling
Copy link
Contributor

@Li-shi-ling Li-shi-ling commented Mar 24, 2026

本次变更为 register_provider_adapter 增加了 i18n 注册路径,与现有的 register_platform_adapter 行为保持一致。

此前,provider 适配器无法通过适配器注册 API 注册自身的 config_metadatai18n_resources,导致 provider 侧动态 i18n 功能不完整。本 PR 填补了这一空缺,完善了后端到前端的 provider i18n 链路,并在 astrbot_plugin_STEmbedding 插件上进行了测试。

Modifications / 改动点

astrbot/core/provider/entities.py

  • ProviderMetaData,增加 config_metadata , i18n_resources参数

astrbot/core/provider/register.py

  • register_provider_adapter(...),增加 config_metadata , i18n_resources 参数,将新的元数据字段传递到 provider 注册存储中

astrbot/dashboard/routes/config.py

  • 添加了用于动态注册元数据的 provider 端 schema 组装逻辑,添加了 provider 端动态国际化翻译收集逻辑,添加了特定 provider 类型的元数据负载生成逻辑,返回 provider_i18n_translations 供前端运行时国际化合并使用,返回 provider_type_metadata,使前端能够按当前 provider 类型合并元数据,而不是全局覆盖共享字段

dashboard/src/composables/useProviderSources.ts

  • 添加了 provider 动态国际化翻译的运行时合并逻辑,当语言环境变化时重新加载 provider 翻译数据,在当前 provider 源 schema 中添加了特定 provider 类型的元数据合并逻辑,更新了 provider 源模板标签,使用已注册的显示/国际化元数据,当后端未提供动态 provider 元数据时保留了回退行为

dashboard/src/components/shared/AstrBotConfig.vue

  • 修改了配置渲染逻辑,同时考虑 schema 定义的字段和现有配置值,确保元数据定义的字段即使在当前配置对象中缺失也能正常渲染,为动态元数据渲染所需的对象/列表/模板列表字段添加了结构化默认初始化

dashboard/src/components/chat/ProviderConfigDialog.vue

  • 将 provider 源表单渲染改为使用特定 provider 类型的合并 schema,保持对话框行为与主 provider 页面一致,避免渲染路径不一致

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

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

5530c3072ae7c873cf2f786c5918f3c7 image image

测试代码节选

from astrbot.api.provider import register_provider_adapter
from astrbot.api.provider import ProviderType
from astrbot.api.provider import EmbeddingProvider

STEMBEDDING_PROVIDER_CONFIG_METADATA = {
    "STEmbedding_path": {
        "type": "string",
        "description": "",
        "hint": "",
    },
    "STEmbedding_torch_load_weights_only": {
        "type": "string",
        "description": "",
        "hint": "",
    },
}
STEMBEDDING_PROVIDER_I18N_RESOURCES = {
    "zh-CN": {
        "name": "STEmbedding",
        "STEmbedding_path": {
            "description": "Sentence Transformers 模型路径",
            "hint": "支持相对路径和绝对路径。相对路径会基于 AstrBot 的数据目录解析。",
        },
        "STEmbedding_torch_load_weights_only": {
            "description": "PyTorch torch.load 的 weights_only 行为",
            "hint": "可选 auto、true、false。PyTorch 2.6+ 默认更严格,旧模型遇到加载失败时可尝试 false,但只建议用于可信模型。",
        },
    },
    "en-US": {
        "name": "STEmbedding",
        "STEmbedding_path": {
            "description": "Sentence Transformers model path",
            "hint": "Supports both relative and absolute paths. Relative paths are resolved from the AstrBot data directory.",
        },
        "STEmbedding_torch_load_weights_only": {
            "description": "PyTorch torch.load weights_only behavior",
            "hint": "Available values: auto, true, false. PyTorch 2.6+ is stricter by default. If an older model fails to load, you can try false, but only for trusted models.",
        },
    },
    "ru-RU": {
        "name": "STEmbedding",
        "STEmbedding_path": {
            "description": "Путь к модели Sentence Transformers",
            "hint": "Поддерживаются относительные и абсолютные пути. Относительные пути вычисляются относительно каталога данных AstrBot.",
        },
        "STEmbedding_torch_load_weights_only": {
            "description": "Поведение параметра weights_only в PyTorch torch.load",
            "hint": "Допустимые значения: auto, true, false. В PyTorch 2.6+ загрузка по умолчанию стала строже. Если старая модель не загружается, можно попробовать false, но только для доверенных моделей.",
        },
    },
}

def register_STEmbeddingProvider():
    try:
        register_provider_adapter(
            "STEmbedding",
            "Sentence Transformers Embedding Provider",
            provider_type=ProviderType.EMBEDDING,
            provider_display_name="STEmbedding",
            default_config_tmpl=STEMBEDDING_PROVIDER_TEMPLATE.copy(),
            config_metadata={
                key: value.copy()
                for key, value in STEMBEDDING_PROVIDER_CONFIG_METADATA.items()
            },
            i18n_resources=STEMBEDDING_PROVIDER_I18N_RESOURCES,
        )(STEmbeddingProvider)
        logger.info("[STEmbedding] Provider 已注册")
    except ValueError:
        logger.info("[STEmbedding] Provider 已存在,跳过注册")

Checklist / 检查清单

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

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

  • [ X ] 🤓 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 文件相应位置。
    没有引入新依赖

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

Summary by Sourcery

Add dynamic i18n metadata and schema support for provider adapters and expose them to the dashboard so provider-specific configuration and translations can be registered and rendered at runtime.

New Features:

  • Allow provider adapters to register i18n resources and configuration metadata via the provider registration API.
  • Expose provider i18n translations and type-specific configuration metadata from the backend config endpoints for use by the Web UI.
  • Support dynamic provider-specific display names in the provider templates based on registered metadata and i18n resources.

Enhancements:

  • Enhance the dashboard provider configuration flows to merge type-specific provider metadata into the base schema and render structured defaults for dynamic object, list, and template-list fields.
  • Update shared configuration components and the chat provider config dialog to use the provider-type-aware schema, aligning their behavior with the main provider configuration page.
  • Extend public provider API exports to include additional provider base classes and the provider adapter registration helper.

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 24, 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!

This pull request significantly enhances the flexibility and internationalization capabilities of provider adapters within the system. By introducing dedicated paths for registering i18n resources and configuration metadata, it allows providers to define their own dynamic UI elements and translations. This change streamlines the process of integrating new providers with rich, localized configuration options, ensuring a consistent and user-friendly experience across different languages and provider types.

Highlights

  • Provider Adapter i18n Registration: Added a new i18n registration path for provider adapters, aligning their behavior with existing platform adapter registration.
  • Dynamic i18n and Configuration Metadata: Enabled provider adapters to register their own config_metadata and i18n_resources, completing the backend-to-frontend dynamic i18n chain for providers.
  • Frontend UI Enhancements: Updated the WebUI to dynamically collect and merge provider-specific i18n translations and configuration metadata, ensuring proper rendering of configuration forms and display names.
  • Configuration Rendering Logic: Improved the configuration rendering logic to account for schema-defined fields and existing configuration values, and to initialize structured default values for object/list/template_list fields.
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.

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 24, 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 - I've found 1 issue, and left some high level feedback:

  • In useProviderSources, the i18n namespace passed to useModuleI18n ('features/config-metadata') differs from the one passed to mergeDynamicTranslations ('features.config-metadata'); aligning these keys will avoid subtle issues where dynamically merged provider translations are not resolved by tmConfigMetadata.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In useProviderSources, the i18n namespace passed to useModuleI18n ('features/config-metadata') differs from the one passed to mergeDynamicTranslations ('features.config-metadata'); aligning these keys will avoid subtle issues where dynamically merged provider translations are not resolved by tmConfigMetadata.

## Individual Comments

### Comment 1
<location path="dashboard/src/composables/useProviderSources.ts" line_range="698" />
<code_context>
+        )
+        metadata["provider_group"]["metadata"]["provider"] = provider_schema["provider"]

         return {
             "metadata": metadata,
             "config": config,
</code_context>
<issue_to_address>
**issue (bug_risk):** Missing `providerSourceSchema` in composable return while dialog expects it.

In `ProviderConfigDialog.vue` you destructure and use `providerSourceSchema` from `useProviderSources`, but that composable only exposes `configSchema`, `providerTemplates`, and `providerTypeMetadata`. As a result, `providerSourceSchema` will be `undefined` and the config UI will fail to render correctly.

Please either expose a computed `providerSourceSchema` from `useProviderSources` (e.g., derived from `configSchema` and `providerTypeMetadata`, similar to how `customSchema` is built in `openConfigDialog`) or update the dialog to use `configSchema` directly if that’s what you intended. The composable’s API and the dialog’s expectations need to be aligned.
</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.

window.removeEventListener('astrbot-locale-changed', loadProviderTemplate)
})

return {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Missing providerSourceSchema in composable return while dialog expects it.

In ProviderConfigDialog.vue you destructure and use providerSourceSchema from useProviderSources, but that composable only exposes configSchema, providerTemplates, and providerTypeMetadata. As a result, providerSourceSchema will be undefined and the config UI will fail to render correctly.

Please either expose a computed providerSourceSchema from useProviderSources (e.g., derived from configSchema and providerTypeMetadata, similar to how customSchema is built in openConfigDialog) or update the dialog to use configSchema directly if that’s what you intended. The composable’s API and the dialog’s expectations need to be aligned.

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 为 Provider 适配器增加了动态注册 config_metadatai18n_resources 的能力,并相应地更新了后端 API 和前端 UI,以支持动态表单生成和国际化。整体实现思路清晰,后端通过新的元数据字段构建了包含 i18n key 的 schema,前端则增加了相应的逻辑来合并和渲染这些动态元数据及翻译。代码改动覆盖了从数据模型、注册机制到 API 路由和前端组件的整个链路,功能实现完整。

我发现了一个可以改进的地方,主要是在前端 Vue 组件中存在直接修改 props 的反模式,这可能会影响代码的可维护性和稳定性。

Comment on lines +170 to +190
function ensureStructuredValue(key, itemMeta) {
if (!props.iterable || typeof props.iterable !== 'object') {
return undefined
}

if (props.iterable[key] !== undefined && props.iterable[key] !== null) {
return props.iterable[key]
}

if (itemMeta?.type === 'object' || itemMeta?.type === 'dict') {
props.iterable[key] = {}
return props.iterable[key]
}

if (itemMeta?.type === 'list' || itemMeta?.type === 'template_list') {
props.iterable[key] = []
return props.iterable[key]
}

return props.iterable[key]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

ensureStructuredValue 函数直接修改了 iterable 这个 prop。在 Vue 中,直接修改 prop 是一种反模式,可能会导致不可预期的行为,并让组件的逻辑变得难以理解。Prop 应当被视为不可变数据。

一个更好的实践是使用一个由 prop 初始化的本地响应式状态,或者让组件通过 emit 事件来通知父组件进行更改。但考虑到这个组件的递归特性,这样做可能会很复杂。一个更简单的、避免直接修改 prop 的修复方法是,确保父组件总是传递一个结构完整的对象,即使这意味着需要在父组件中初始化空对象/数组。

当前实现虽然能工作,但比较脆弱,未来可能引入 bug。

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd1abfc77a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1495 to +1499
if provider.i18n_resources:
self._apply_dynamic_i18n_keys(
provider_items_to_inject,
f"provider_group.provider.{provider.type}",
)

Choose a reason for hiding this comment

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

P2 Badge Preserve provider metadata fallback when i18n locale is absent

This rewrites dynamic provider metadata to i18n keys whenever i18n_resources is present, but it does not keep a readable fallback for locales that the plugin does not provide. In that case (for example, plugin only ships zh-CN while UI is en-US), the frontend receives key paths without corresponding translations and renders raw strings like provider_group.provider.xxx.field.hint, making provider-source forms hard to use. Keep original metadata text as fallback (or gate key-rewriting on available locale data) so partially localized providers remain usable.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a61a1a2f5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +50 to +53
for (const key of Object.keys(metadataItems)) {
if (key === 'hint') continue
result[key] = iterable[key]
}

Choose a reason for hiding this comment

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

P1 Badge Render only intended keys in AstrBotConfig forms

This loop now prioritizes all metadata keys instead of the keys actually present in iterable, which breaks callers that intentionally pass a subset object (for example basicSourceConfig/advancedSourceConfig in the provider source UI). Because provider metadata contains fields for many provider types, the form will render unrelated inputs and can push users into editing or saving irrelevant settings; previously only configured keys were shown. Restricting rendered keys to the provided iterable (or making metadata-driven expansion opt-in) avoids this regression.

Useful? React with 👍 / 👎.

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.

1 participant