fix(audit): P0 反思引擎 LLM 接入 + P1 hand run_id/skill triggers/pipeline v2
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
审计修复 Batch 1 (M4-02/M3-01/M5-01/M6-02):
P0 M4-02: reflection_reflect 从 KernelState 获取 LLM driver
- 新增 kernel_state 参数,从 kernel.driver() 获取驱动
- 自动路径(post_conversation_hook)已正常,手动 Tauri 命令路径已修复
P1 M3-01: hand_execute 返回 run_id 给前端
- HandResult 新增 run_id 字段
- execute_hand 结果包含 run_id.to_string()
P1 M5-01: skill-discovery 使用后端 triggers 字段
- BackendSkillInfo 新增 triggers 字段
- convertFromBackend 优先使用 triggers,fallback tags
P1 M6-02: pipeline_list 支持 v2 YAML 格式
- scan_pipelines_with_paths 增加 v2 fallback 解析
- 新增 pipeline_v2_to_info 转换函数
- discovery.rs 导入 parse_pipeline_v2_yaml
注: M4-01 双数据库问题已在之前批次修复
M6-01 route_intent 已确认注册,审计结论过时
This commit is contained in:
@@ -760,9 +760,14 @@ pub async fn reflection_reflect(
|
|||||||
agent_id: String,
|
agent_id: String,
|
||||||
memories: Vec<MemoryEntryForAnalysis>,
|
memories: Vec<MemoryEntryForAnalysis>,
|
||||||
state: tauri::State<'_, ReflectionEngineState>,
|
state: tauri::State<'_, ReflectionEngineState>,
|
||||||
|
kernel_state: tauri::State<'_, crate::kernel_commands::KernelState>,
|
||||||
) -> Result<ReflectionResult, String> {
|
) -> Result<ReflectionResult, String> {
|
||||||
|
let driver = {
|
||||||
|
let kernel_lock = kernel_state.lock().await;
|
||||||
|
kernel_lock.as_ref().map(|k| k.driver())
|
||||||
|
};
|
||||||
let mut engine = state.lock().await;
|
let mut engine = state.lock().await;
|
||||||
Ok(engine.reflect(&agent_id, &memories, None).await)
|
Ok(engine.reflect(&agent_id, &memories, driver).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get reflection history
|
/// Get reflection history
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ pub struct HandResult {
|
|||||||
pub output: serde_json::Value,
|
pub output: serde_json::Value,
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
pub duration_ms: Option<u64>,
|
pub duration_ms: Option<u64>,
|
||||||
|
pub run_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<zclaw_hands::HandResult> for HandResult {
|
impl From<zclaw_hands::HandResult> for HandResult {
|
||||||
@@ -102,6 +103,7 @@ impl From<zclaw_hands::HandResult> for HandResult {
|
|||||||
output: result.output,
|
output: result.output,
|
||||||
error: result.error,
|
error: result.error,
|
||||||
duration_ms: result.duration_ms,
|
duration_ms: result.duration_ms,
|
||||||
|
run_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +157,7 @@ pub async fn hand_execute(
|
|||||||
}),
|
}),
|
||||||
error: None,
|
error: None,
|
||||||
duration_ms: None,
|
duration_ms: None,
|
||||||
|
run_id: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,16 +178,19 @@ pub async fn hand_execute(
|
|||||||
}),
|
}),
|
||||||
error: None,
|
error: None,
|
||||||
duration_ms: None,
|
duration_ms: None,
|
||||||
|
run_id: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute hand directly (returns result + run_id for tracking)
|
// Execute hand directly (returns result + run_id for tracking)
|
||||||
let (result, _run_id) = kernel.execute_hand(&id, input).await
|
let (result, run_id) = kernel.execute_hand(&id, input).await
|
||||||
.map_err(|e| format!("Failed to execute hand: {}", e))?;
|
.map_err(|e| format!("Failed to execute hand: {}", e))?;
|
||||||
|
|
||||||
Ok(HandResult::from(result))
|
let mut hand_result = HandResult::from(result);
|
||||||
|
hand_result.run_id = Some(run_id.to_string());
|
||||||
|
Ok(hand_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Approve a hand execution
|
/// Approve a hand execution
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use tauri::{AppHandle, Emitter, State};
|
|||||||
use zclaw_pipeline::{
|
use zclaw_pipeline::{
|
||||||
RunStatus,
|
RunStatus,
|
||||||
parse_pipeline_yaml,
|
parse_pipeline_yaml,
|
||||||
|
parse_pipeline_v2_yaml,
|
||||||
PipelineExecutor,
|
PipelineExecutor,
|
||||||
ActionRegistry,
|
ActionRegistry,
|
||||||
LlmActionDriver,
|
LlmActionDriver,
|
||||||
@@ -15,7 +16,7 @@ use zclaw_pipeline::{
|
|||||||
|
|
||||||
use super::{PipelineState, PipelineInfo, PipelineRunResponse, RunPipelineResponse, RunPipelineRequest};
|
use super::{PipelineState, PipelineInfo, PipelineRunResponse, RunPipelineResponse, RunPipelineRequest};
|
||||||
use super::adapters::{RuntimeLlmAdapter, PipelineSkillDriver, PipelineHandDriver};
|
use super::adapters::{RuntimeLlmAdapter, PipelineSkillDriver, PipelineHandDriver};
|
||||||
use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info};
|
use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info, pipeline_v2_to_info};
|
||||||
|
|
||||||
use crate::kernel_commands::KernelState;
|
use crate::kernel_commands::KernelState;
|
||||||
|
|
||||||
@@ -60,6 +61,8 @@ pub async fn pipeline_list(
|
|||||||
state_pipelines.insert(info.id.clone(), pipeline);
|
state_pipelines.insert(info.id.clone(), pipeline);
|
||||||
state_paths.insert(info.id.clone(), path.clone());
|
state_paths.insert(info.id.clone(), path.clone());
|
||||||
}
|
}
|
||||||
|
// v2 pipelines are listed but not stored in v1 state;
|
||||||
|
// they can be discovered and displayed but execution requires v2 engine support.
|
||||||
}
|
}
|
||||||
result.push(info.clone());
|
result.push(info.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use std::path::PathBuf;
|
|||||||
use zclaw_pipeline::{
|
use zclaw_pipeline::{
|
||||||
Pipeline,
|
Pipeline,
|
||||||
parse_pipeline_yaml,
|
parse_pipeline_yaml,
|
||||||
|
parse_pipeline_v2_yaml,
|
||||||
|
PipelineV2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::types::{PipelineInfo, PipelineInputInfo};
|
use super::types::{PipelineInfo, PipelineInputInfo};
|
||||||
@@ -88,8 +90,29 @@ pub(crate) fn scan_pipelines_with_paths(
|
|||||||
tracing::debug!("[scan] Found pipeline: {} at {:?}", pipeline.metadata.name, path);
|
tracing::debug!("[scan] Found pipeline: {} at {:?}", pipeline.metadata.name, path);
|
||||||
pipelines.push((pipeline_to_info(&pipeline), path));
|
pipelines.push((pipeline_to_info(&pipeline), path));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
tracing::error!("[scan] Failed to parse pipeline at {:?}: {}", path, e);
|
// Try v2 parser as fallback
|
||||||
|
match parse_pipeline_v2_yaml(&content) {
|
||||||
|
Ok(v2) => {
|
||||||
|
tracing::debug!(
|
||||||
|
"[scan] Parsed v2 YAML: {} -> category: {:?}",
|
||||||
|
v2.metadata.name,
|
||||||
|
v2.metadata.category
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(filter) = category_filter {
|
||||||
|
if v2.metadata.category.as_deref() != Some(filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::debug!("[scan] Found v2 pipeline: {} at {:?}", v2.metadata.name, path);
|
||||||
|
pipelines.push((pipeline_v2_to_info(&v2), path));
|
||||||
|
}
|
||||||
|
Err(e2) => {
|
||||||
|
tracing::error!("[scan] Failed to parse pipeline at {:?}: v1 error, v2: {}", path, e2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +140,8 @@ pub(crate) fn scan_pipelines_full_sync(
|
|||||||
if let Ok(pipeline) = parse_pipeline_yaml(&content) {
|
if let Ok(pipeline) = parse_pipeline_yaml(&content) {
|
||||||
pipelines.push((path, pipeline));
|
pipelines.push((path, pipeline));
|
||||||
}
|
}
|
||||||
|
// v2 pipelines are not stored in the v1 state vector;
|
||||||
|
// scan_pipelines_with_paths handles v2 discovery separately.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,3 +190,40 @@ pub(crate) fn pipeline_to_info(pipeline: &Pipeline) -> PipelineInfo {
|
|||||||
}).collect(),
|
}).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a PipelineV2 to PipelineInfo for listing purposes.
|
||||||
|
/// V2 pipelines are discoverable but not stored in state (no v1 Pipeline to execute).
|
||||||
|
pub(crate) fn pipeline_v2_to_info(v2: &PipelineV2) -> PipelineInfo {
|
||||||
|
use zclaw_pipeline::ParamType;
|
||||||
|
PipelineInfo {
|
||||||
|
id: v2.metadata.name.clone(),
|
||||||
|
display_name: v2.metadata.display_name.clone()
|
||||||
|
.unwrap_or_else(|| v2.metadata.name.clone()),
|
||||||
|
description: v2.metadata.description.clone().unwrap_or_default(),
|
||||||
|
category: v2.metadata.category.clone().unwrap_or_default(),
|
||||||
|
industry: v2.metadata.industry.clone().unwrap_or_default(),
|
||||||
|
tags: v2.metadata.tags.clone(),
|
||||||
|
icon: v2.metadata.icon.clone().unwrap_or_else(|| "📦".to_string()),
|
||||||
|
version: v2.metadata.version.clone(),
|
||||||
|
author: String::new(),
|
||||||
|
inputs: v2.params.iter().map(|param| {
|
||||||
|
PipelineInputInfo {
|
||||||
|
name: param.name.clone(),
|
||||||
|
input_type: match ¶m.param_type {
|
||||||
|
ParamType::String => "string".to_string(),
|
||||||
|
ParamType::Number => "number".to_string(),
|
||||||
|
ParamType::Boolean => "boolean".to_string(),
|
||||||
|
ParamType::Select => "select".to_string(),
|
||||||
|
ParamType::MultiSelect => "multi-select".to_string(),
|
||||||
|
ParamType::File => "file".to_string(),
|
||||||
|
ParamType::Text => "text".to_string(),
|
||||||
|
},
|
||||||
|
required: param.required,
|
||||||
|
label: param.label.clone().unwrap_or_else(|| param.name.clone()),
|
||||||
|
placeholder: param.placeholder.clone(),
|
||||||
|
default: param.default.clone(),
|
||||||
|
options: param.options.clone(),
|
||||||
|
}
|
||||||
|
}).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ interface BackendSkillInfo {
|
|||||||
version: string;
|
version: string;
|
||||||
capabilities: string[];
|
capabilities: string[];
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
triggers: string[];
|
||||||
mode: string;
|
mode: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
@@ -113,10 +114,10 @@ export class SkillDiscoveryEngine {
|
|||||||
name: backend.name,
|
name: backend.name,
|
||||||
description: backend.description,
|
description: backend.description,
|
||||||
version: backend.version,
|
version: backend.version,
|
||||||
triggers: backend.tags, // Use tags as triggers
|
triggers: backend.triggers?.length ? backend.triggers : backend.tags,
|
||||||
capabilities: backend.capabilities,
|
capabilities: backend.capabilities,
|
||||||
mode: backend.mode,
|
mode: backend.mode,
|
||||||
toolDeps: [], // Backend doesn't have this field
|
toolDeps: [],
|
||||||
installed: backend.enabled,
|
installed: backend.enabled,
|
||||||
category: backend.tags[0] || 'general',
|
category: backend.tags[0] || 'general',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user