feat: Add LLM Provider support in plugin runtime

- Introduced LLM Provider declarations in plugin manifests, allowing plugins to specify their LLM capabilities.
- Implemented validation for LLM Provider declarations to prevent duplicates and conflicts.
- Enhanced the PluginRunner to handle LLM Provider invocation requests, enabling plugins to interact with LLM Providers seamlessly.
- Added a ClientRegistry to manage LLM Provider registrations and ensure no conflicts arise between different plugins.
- Created a PluginLLMClient to facilitate communication with LLM Providers through the plugin runtime.
- Developed tests to ensure proper registration and conflict handling of LLM Providers.
This commit is contained in:
DrSmoothl
2026-04-27 16:49:44 +08:00
parent 1fe9dc8786
commit 742e21a727
11 changed files with 903 additions and 13 deletions

View File

@@ -148,6 +148,35 @@ class PluginRuntimeManager(
validator = ManifestValidator(validate_python_package_dependencies=False)
return validator.build_plugin_dependency_map(plugin_dirs)
@classmethod
def _discover_llm_provider_conflicts(cls, plugin_dirs: Iterable[Path]) -> Dict[str, str]:
"""扫描插件 Manifest发现 LLM Provider client_type 冲突。
Args:
plugin_dirs: 需要扫描的插件根目录集合。
Returns:
Dict[str, str]: 需要阻止加载的插件 ID 与原因映射。
"""
validator = ManifestValidator(validate_python_package_dependencies=False)
provider_owners: Dict[str, List[str]] = {}
for _plugin_path, manifest in validator.iter_plugin_manifests(plugin_dirs, require_entrypoint=True):
for client_type in manifest.llm_provider_client_types:
provider_owners.setdefault(client_type, []).append(manifest.id)
blocked_reasons: Dict[str, str] = {}
for client_type, plugin_ids in provider_owners.items():
unique_plugin_ids = sorted(set(plugin_ids))
if len(unique_plugin_ids) <= 1:
continue
reason = (
f"LLM Provider client_type 冲突: {client_type} 被以下插件重复声明: "
f"{', '.join(unique_plugin_ids)}"
)
for plugin_id in unique_plugin_ids:
blocked_reasons[plugin_id] = reason
return blocked_reasons
@classmethod
def _build_group_start_order(
cls,
@@ -271,7 +300,11 @@ class PluginRuntimeManager(
"""
result = await self._plugin_dependency_pipeline.execute(plugin_dirs)
changed_plugin_ids = self._set_blocked_plugin_reasons(result.blocked_plugin_reasons)
blocked_plugin_reasons = {
**result.blocked_plugin_reasons,
**self._discover_llm_provider_conflicts(plugin_dirs),
}
changed_plugin_ids = self._set_blocked_plugin_reasons(blocked_plugin_reasons)
return DependencySyncState(
blocked_changed_plugin_ids=changed_plugin_ids,
environment_changed=result.environment_changed,