Files
proxycast/src-tauri/src/processor/steps/plugin.rs
T
coso aca666ea1d feat: v0.10.0 - 配置与凭证统一导入导出功能
主要更新:
- 实现以 YAML 配置文件为单一数据源的统一配置管理
- 支持凭证池配置同步到 YAML 文件
- 实现配置和凭证的统一导入导出
- 支持 OAuth Token 文件存储在可配置的 auth-dir 目录
- 实现配置热重载时自动同步凭证池
- 添加导出脱敏功能保护敏感信息
- 支持导入时合并或替换模式
2025-12-17 12:00:54 +08:00

171 lines
4.9 KiB
Rust

//! 插件钩子步骤
//!
//! 执行插件的前置和后置钩子
use super::traits::{PipelineStep, StepError};
use crate::plugin::PluginManager;
use crate::processor::RequestContext;
use crate::ProviderType;
use async_trait::async_trait;
use std::sync::Arc;
/// 插件前置钩子步骤
///
/// 在 Provider 调用前执行所有启用插件的 on_request 钩子
pub struct PluginPreStep {
/// 插件管理器
plugins: Arc<PluginManager>,
}
impl PluginPreStep {
/// 创建新的插件前置步骤
pub fn new(plugins: Arc<PluginManager>) -> Self {
Self { plugins }
}
}
#[async_trait]
impl PipelineStep for PluginPreStep {
async fn execute(
&self,
ctx: &mut RequestContext,
payload: &mut serde_json::Value,
) -> Result<(), StepError> {
// 初始化插件上下文
let provider = ctx.provider.unwrap_or(ProviderType::Kiro);
ctx.init_plugin_context(provider);
// 获取插件上下文的可变引用
if let Some(plugin_ctx) = ctx.plugin_context_mut() {
let results = self.plugins.run_on_request(plugin_ctx, payload).await;
// 检查是否有失败的钩子
for result in &results {
if !result.success {
tracing::warn!("[PLUGIN] on_request hook failed: {:?}", result.error);
// 插件失败不阻止请求继续,只记录警告
}
}
// 记录插件执行结果到元数据
ctx.set_metadata(
"plugin_pre_results",
serde_json::json!(results
.iter()
.map(|r| serde_json::json!({
"success": r.success,
"modified": r.modified,
"duration_ms": r.duration_ms
}))
.collect::<Vec<_>>()),
);
}
Ok(())
}
fn name(&self) -> &str {
"plugin_pre"
}
}
/// 插件后置钩子步骤
///
/// 在 Provider 调用后执行所有启用插件的 on_response 钩子
pub struct PluginPostStep {
/// 插件管理器
plugins: Arc<PluginManager>,
}
impl PluginPostStep {
/// 创建新的插件后置步骤
pub fn new(plugins: Arc<PluginManager>) -> Self {
Self { plugins }
}
/// 执行错误钩子
pub async fn run_on_error(&self, ctx: &mut RequestContext, error: &str) {
if let Some(plugin_ctx) = ctx.plugin_context_mut() {
let results = self.plugins.run_on_error(plugin_ctx, error).await;
for result in &results {
if !result.success {
tracing::warn!("[PLUGIN] on_error hook failed: {:?}", result.error);
}
}
}
}
}
#[async_trait]
impl PipelineStep for PluginPostStep {
async fn execute(
&self,
ctx: &mut RequestContext,
payload: &mut serde_json::Value,
) -> Result<(), StepError> {
if let Some(plugin_ctx) = ctx.plugin_context_mut() {
let results = self.plugins.run_on_response(plugin_ctx, payload).await;
// 检查是否有失败的钩子
for result in &results {
if !result.success {
tracing::warn!("[PLUGIN] on_response hook failed: {:?}", result.error);
}
}
// 记录插件执行结果到元数据
ctx.set_metadata(
"plugin_post_results",
serde_json::json!(results
.iter()
.map(|r| serde_json::json!({
"success": r.success,
"modified": r.modified,
"duration_ms": r.duration_ms
}))
.collect::<Vec<_>>()),
);
}
Ok(())
}
fn name(&self) -> &str {
"plugin_post"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_plugin_pre_step_execute() {
let plugins = Arc::new(PluginManager::with_defaults());
let step = PluginPreStep::new(plugins);
let mut ctx = RequestContext::new("model".to_string());
ctx.set_provider(ProviderType::Kiro);
let mut payload = serde_json::json!({"model": "model"});
let result = step.execute(&mut ctx, &mut payload).await;
assert!(result.is_ok());
assert!(ctx.plugin_ctx.is_some());
}
#[tokio::test]
async fn test_plugin_post_step_execute() {
let plugins = Arc::new(PluginManager::with_defaults());
let step = PluginPostStep::new(plugins);
let mut ctx = RequestContext::new("model".to_string());
ctx.set_provider(ProviderType::Kiro);
ctx.init_plugin_context(ProviderType::Kiro);
let mut payload = serde_json::json!({"response": "test"});
let result = step.execute(&mut ctx, &mut payload).await;
assert!(result.is_ok());
}
}