1use async_trait::async_trait;
53
54use super::error::SkillError;
55use super::types::{SkillDefinition, SkillExecutionMode, SkillExecutionResult};
56
57#[async_trait]
96pub trait LlmProvider: Send + Sync {
97 async fn chat(
114 &self,
115 system_prompt: &str,
116 user_message: &str,
117 model: Option<&str>,
118 ) -> Result<String, SkillError>;
119
120 async fn chat_stream(
154 &self,
155 system_prompt: &str,
156 user_message: &str,
157 model: Option<&str>,
158 _callback: Box<dyn Fn(&str) + Send>,
159 ) -> Result<String, SkillError> {
160 self.chat(system_prompt, user_message, model).await
162 }
163}
164
165pub trait ExecutionCallback: Send + Sync {
214 fn on_step_start(&self, step_id: &str, step_name: &str, total_steps: usize);
222
223 fn on_step_complete(&self, step_id: &str, output: &str);
230
231 fn on_step_error(&self, step_id: &str, error: &str, will_retry: bool);
239
240 fn on_complete(&self, success: bool, final_output: Option<&str>);
247}
248
249#[derive(Debug, Clone, Copy, Default)]
263pub struct NoopCallback;
264
265impl ExecutionCallback for NoopCallback {
266 fn on_step_start(&self, _step_id: &str, _step_name: &str, _total_steps: usize) {}
267
268 fn on_step_complete(&self, _step_id: &str, _output: &str) {}
269
270 fn on_step_error(&self, _step_id: &str, _error: &str, _will_retry: bool) {}
271
272 fn on_complete(&self, _success: bool, _final_output: Option<&str>) {}
273}
274
275pub struct SkillExecutor<P: LlmProvider> {
316 provider: P,
317}
318
319impl<P: LlmProvider> SkillExecutor<P> {
320 pub fn new(provider: P) -> Self {
336 Self { provider }
337 }
338
339 pub fn provider(&self) -> &P {
345 &self.provider
346 }
347
348 pub async fn execute(
381 &self,
382 skill: &SkillDefinition,
383 input: &str,
384 callback: Option<&dyn ExecutionCallback>,
385 ) -> Result<SkillExecutionResult, SkillError> {
386 let noop = NoopCallback;
388 let callback = callback.unwrap_or(&noop);
389
390 match skill.execution_mode {
392 SkillExecutionMode::Prompt => self.execute_prompt_mode(skill, input, callback).await,
393 SkillExecutionMode::Workflow => {
394 self.execute_workflow_mode(skill, input, callback).await
395 }
396 SkillExecutionMode::Agent => Err(SkillError::not_implemented(
397 "Agent 模式尚未实现,请使用 Prompt 或 Workflow 模式",
398 )),
399 }
400 }
401
402 pub(crate) async fn execute_prompt_mode(
421 &self,
422 skill: &SkillDefinition,
423 input: &str,
424 callback: &dyn ExecutionCallback,
425 ) -> Result<SkillExecutionResult, SkillError> {
426 callback.on_step_start("prompt", &skill.display_name, 1);
428
429 let model = skill.model.as_deref();
431 let result = self
432 .provider
433 .chat(&skill.markdown_content, input, model)
434 .await;
435
436 match result {
437 Ok(output) => {
438 callback.on_step_complete("prompt", &output);
440 callback.on_complete(true, Some(&output));
441
442 Ok(SkillExecutionResult {
443 success: true,
444 output: Some(output),
445 error: None,
446 steps_completed: vec![],
447 command_name: Some(skill.skill_name.clone()),
448 allowed_tools: skill.allowed_tools.clone(),
449 model: skill.model.clone(),
450 })
451 }
452 Err(e) => {
453 let error_msg = e.to_string();
455 callback.on_step_error("prompt", &error_msg, false);
456 callback.on_complete(false, None);
457
458 Ok(SkillExecutionResult {
459 success: false,
460 output: None,
461 error: Some(error_msg),
462 steps_completed: vec![],
463 command_name: Some(skill.skill_name.clone()),
464 allowed_tools: skill.allowed_tools.clone(),
465 model: skill.model.clone(),
466 })
467 }
468 }
469 }
470
471 pub(crate) async fn execute_workflow_mode(
502 &self,
503 skill: &SkillDefinition,
504 input: &str,
505 callback: &dyn ExecutionCallback,
506 ) -> Result<SkillExecutionResult, SkillError> {
507 use super::types::StepResult;
508 use super::workflow::{interpolate_variables, topological_sort};
509 use std::collections::HashMap;
510 use tracing::{debug, error, info, warn};
511
512 let workflow = skill.workflow.as_ref().ok_or_else(|| {
514 callback.on_complete(false, None);
515 SkillError::invalid_config("Workflow 模式需要定义 workflow 字段")
516 })?;
517
518 info!(
519 skill_name = %skill.skill_name,
520 steps_count = workflow.steps.len(),
521 max_retries = workflow.max_retries,
522 continue_on_failure = workflow.continue_on_failure,
523 "开始执行 Workflow 模式"
524 );
525
526 let sorted_steps = topological_sort(&workflow.steps).inspect_err(|_| {
528 callback.on_complete(false, None);
529 })?;
530
531 let total_steps = sorted_steps.len();
532 debug!(total_steps = total_steps, "拓扑排序完成");
533
534 let mut context: HashMap<String, String> = HashMap::new();
536 context.insert("user_input".to_string(), input.to_string());
537
538 let mut steps_completed: Vec<StepResult> = Vec::with_capacity(total_steps);
540 let mut final_output: Option<String> = None;
541 let mut had_failure = false;
542
543 for step in sorted_steps {
545 let interpolated_prompt = interpolate_variables(&step.prompt, &context);
547 debug!(step_id = %step.id, "执行步骤,变量插值完成");
548
549 match self
551 .execute_step_with_retry(
552 step,
553 &interpolated_prompt,
554 workflow.max_retries,
555 total_steps,
556 callback,
557 )
558 .await
559 {
560 Ok(output) => {
561 info!(step_id = %step.id, output_len = output.len(), "步骤执行成功");
562 context.insert(step.output.clone(), output.clone());
564 context.insert(format!("{}.output", step.id), output.clone());
565 callback.on_step_complete(&step.id, &output);
566 steps_completed.push(StepResult::success(&step.id, &step.name, &output));
567 final_output = Some(output);
568 }
569 Err(e) => {
570 let error_msg = e.to_string();
571 error!(step_id = %step.id, error = %error_msg, "步骤执行失败");
572 had_failure = true;
573 steps_completed.push(StepResult::failure(&step.id, &step.name, &error_msg));
574
575 if workflow.continue_on_failure {
576 warn!(step_id = %step.id, "continue_on_failure=true,继续执行");
577 context.insert(step.output.clone(), String::new());
578 context.insert(format!("{}.output", step.id), String::new());
579 } else {
580 callback.on_complete(false, None);
581 return Ok(SkillExecutionResult {
582 success: false,
583 output: None,
584 error: Some(format!("步骤 '{}' 执行失败: {}", step.id, error_msg)),
585 steps_completed,
586 command_name: Some(skill.skill_name.clone()),
587 allowed_tools: skill.allowed_tools.clone(),
588 model: skill.model.clone(),
589 });
590 }
591 }
592 }
593 }
594
595 let success = !had_failure;
597 info!(
598 success = success,
599 steps_completed = steps_completed.len(),
600 "Workflow 执行完成"
601 );
602
603 callback.on_complete(success, final_output.as_deref());
604
605 Ok(SkillExecutionResult {
606 success,
607 output: final_output,
608 error: if had_failure {
609 Some("部分步骤执行失败".to_string())
610 } else {
611 None
612 },
613 steps_completed,
614 command_name: Some(skill.skill_name.clone()),
615 allowed_tools: skill.allowed_tools.clone(),
616 model: skill.model.clone(),
617 })
618 }
619
620 pub(crate) async fn execute_step_with_retry(
669 &self,
670 step: &super::types::WorkflowStep,
671 interpolated_prompt: &str,
672 max_retries: u32,
673 total_steps: usize,
674 callback: &dyn ExecutionCallback,
675 ) -> Result<String, SkillError> {
676 use std::time::Duration;
677 use tokio::time::sleep;
678 use tracing::{info, warn};
679
680 const BASE_DELAY_MS: u64 = 100;
682
683 callback.on_step_start(&step.id, &step.name, total_steps);
685
686 let mut last_error: Option<SkillError> = None;
687
688 for attempt in 0..=max_retries {
691 if attempt > 0 {
693 let delay_ms = BASE_DELAY_MS * (1 << (attempt - 1)); let delay = Duration::from_millis(delay_ms);
695
696 warn!(
697 step_id = %step.id,
698 attempt = attempt,
699 max_retries = max_retries,
700 delay_ms = delay_ms,
701 "步骤执行失败,等待后重试"
702 );
703
704 sleep(delay).await;
705 }
706
707 info!(
708 step_id = %step.id,
709 step_name = %step.name,
710 attempt = attempt,
711 "执行步骤"
712 );
713
714 match self.provider.chat("", interpolated_prompt, None).await {
717 Ok(output) => {
718 info!(
720 step_id = %step.id,
721 attempt = attempt,
722 "步骤执行成功"
723 );
724 return Ok(output);
725 }
726 Err(e) => {
727 let error_msg = e.to_string();
729 last_error = Some(e);
730
731 let will_retry = attempt < max_retries;
733
734 callback.on_step_error(&step.id, &error_msg, will_retry);
736
737 if will_retry {
738 warn!(
739 step_id = %step.id,
740 attempt = attempt,
741 max_retries = max_retries,
742 error = %error_msg,
743 "步骤执行失败,将进行重试"
744 );
745 } else {
746 warn!(
747 step_id = %step.id,
748 attempt = attempt,
749 max_retries = max_retries,
750 error = %error_msg,
751 "步骤执行失败,重试次数已耗尽"
752 );
753 }
754 }
755 }
756 }
757
758 Err(last_error.unwrap_or_else(|| {
760 SkillError::execution_failed(format!("步骤 '{}' 执行失败", step.id))
761 }))
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use std::sync::atomic::{AtomicUsize, Ordering};
769 use std::sync::Arc;
770
771 struct MockProvider {
775 response: String,
776 should_fail: bool,
777 }
778
779 impl MockProvider {
780 fn new(response: &str) -> Self {
781 Self {
782 response: response.to_string(),
783 should_fail: false,
784 }
785 }
786
787 fn failing() -> Self {
788 Self {
789 response: String::new(),
790 should_fail: true,
791 }
792 }
793 }
794
795 #[async_trait]
796 impl LlmProvider for MockProvider {
797 async fn chat(
798 &self,
799 _system_prompt: &str,
800 _user_message: &str,
801 _model: Option<&str>,
802 ) -> Result<String, SkillError> {
803 if self.should_fail {
804 Err(SkillError::provider_error("Mock provider error"))
805 } else {
806 Ok(self.response.clone())
807 }
808 }
809 }
810
811 struct MockCallback {
815 step_start_count: AtomicUsize,
816 step_complete_count: AtomicUsize,
817 step_error_count: AtomicUsize,
818 complete_count: AtomicUsize,
819 }
820
821 impl MockCallback {
822 fn new() -> Self {
823 Self {
824 step_start_count: AtomicUsize::new(0),
825 step_complete_count: AtomicUsize::new(0),
826 step_error_count: AtomicUsize::new(0),
827 complete_count: AtomicUsize::new(0),
828 }
829 }
830 }
831
832 impl ExecutionCallback for MockCallback {
833 fn on_step_start(&self, _step_id: &str, _step_name: &str, _total_steps: usize) {
834 self.step_start_count.fetch_add(1, Ordering::SeqCst);
835 }
836
837 fn on_step_complete(&self, _step_id: &str, _output: &str) {
838 self.step_complete_count.fetch_add(1, Ordering::SeqCst);
839 }
840
841 fn on_step_error(&self, _step_id: &str, _error: &str, _will_retry: bool) {
842 self.step_error_count.fetch_add(1, Ordering::SeqCst);
843 }
844
845 fn on_complete(&self, _success: bool, _final_output: Option<&str>) {
846 self.complete_count.fetch_add(1, Ordering::SeqCst);
847 }
848 }
849
850 #[tokio::test]
853 async fn test_mock_provider_chat_success() {
854 let provider = MockProvider::new("Hello, world!");
855 let result = provider
856 .chat("System prompt", "User message", Some("gpt-4"))
857 .await;
858
859 assert!(result.is_ok());
860 assert_eq!(result.unwrap(), "Hello, world!");
861 }
862
863 #[tokio::test]
864 async fn test_mock_provider_chat_failure() {
865 let provider = MockProvider::failing();
866 let result = provider.chat("System prompt", "User message", None).await;
867
868 assert!(result.is_err());
869 let err = result.unwrap_err();
870 assert!(err.is_provider_error());
871 }
872
873 #[tokio::test]
874 async fn test_chat_stream_default_implementation() {
875 let provider = MockProvider::new("Streamed response");
876 let callback = Box::new(|_chunk: &str| {
877 });
879
880 let result = provider
881 .chat_stream("System prompt", "User message", Some("gpt-4"), callback)
882 .await;
883
884 assert!(result.is_ok());
885 assert_eq!(result.unwrap(), "Streamed response");
886 }
887
888 #[tokio::test]
889 async fn test_chat_with_none_model() {
890 let provider = MockProvider::new("Response");
891 let result = provider.chat("System", "User", None).await;
892
893 assert!(result.is_ok());
894 }
895
896 #[tokio::test]
897 async fn test_chat_with_empty_prompts() {
898 let provider = MockProvider::new("Response");
899 let result = provider.chat("", "", None).await;
900
901 assert!(result.is_ok());
902 }
903
904 #[test]
907 fn test_mock_callback_step_start() {
908 let callback = MockCallback::new();
909 callback.on_step_start("step1", "Test Step", 3);
910
911 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
912 }
913
914 #[test]
915 fn test_mock_callback_step_complete() {
916 let callback = MockCallback::new();
917 callback.on_step_complete("step1", "Output content");
918
919 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 1);
920 }
921
922 #[test]
923 fn test_mock_callback_step_error() {
924 let callback = MockCallback::new();
925 callback.on_step_error("step1", "Error message", true);
926
927 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
928 }
929
930 #[test]
931 fn test_mock_callback_complete() {
932 let callback = MockCallback::new();
933 callback.on_complete(true, Some("Final output"));
934
935 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
936 }
937
938 #[test]
939 fn test_mock_callback_multiple_calls() {
940 let callback = MockCallback::new();
941
942 callback.on_step_start("step1", "Step 1", 3);
943 callback.on_step_complete("step1", "Output 1");
944 callback.on_step_start("step2", "Step 2", 3);
945 callback.on_step_error("step2", "Error", true);
946 callback.on_step_start("step2", "Step 2 retry", 3);
947 callback.on_step_complete("step2", "Output 2");
948 callback.on_complete(true, Some("Final"));
949
950 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 3);
951 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 2);
952 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
953 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
954 }
955
956 #[test]
959 fn test_noop_callback_step_start() {
960 let callback = NoopCallback;
961 callback.on_step_start("step1", "Test Step", 3);
963 }
964
965 #[test]
966 fn test_noop_callback_step_complete() {
967 let callback = NoopCallback;
968 callback.on_step_complete("step1", "Output content");
969 }
970
971 #[test]
972 fn test_noop_callback_step_error() {
973 let callback = NoopCallback;
974 callback.on_step_error("step1", "Error message", true);
975 }
976
977 #[test]
978 fn test_noop_callback_complete() {
979 let callback = NoopCallback;
980 callback.on_complete(true, Some("Final output"));
981 }
982
983 #[test]
984 fn test_noop_callback_complete_with_none() {
985 let callback = NoopCallback;
986 callback.on_complete(false, None);
987 }
988
989 #[test]
990 fn test_noop_callback_is_default() {
991 let callback = NoopCallback;
992 callback.on_step_start("step1", "Test", 1);
993 }
994
995 #[test]
996 fn test_noop_callback_is_clone() {
997 let callback = NoopCallback;
998 let cloned = callback;
999 cloned.on_step_start("step1", "Test", 1);
1000 }
1001
1002 #[test]
1003 fn test_noop_callback_debug() {
1004 let callback = NoopCallback;
1005 let debug_str = format!("{:?}", callback);
1006 assert!(debug_str.contains("NoopCallback"));
1007 }
1008
1009 #[test]
1012 fn test_mock_provider_is_send_sync() {
1013 fn assert_send_sync<T: Send + Sync>() {}
1014 assert_send_sync::<MockProvider>();
1015 }
1016
1017 #[test]
1018 fn test_mock_callback_is_send_sync() {
1019 fn assert_send_sync<T: Send + Sync>() {}
1020 assert_send_sync::<MockCallback>();
1021 }
1022
1023 #[test]
1024 fn test_noop_callback_is_send_sync() {
1025 fn assert_send_sync<T: Send + Sync>() {}
1026 assert_send_sync::<NoopCallback>();
1027 }
1028
1029 #[test]
1032 fn test_callback_with_arc() {
1033 let callback = Arc::new(MockCallback::new());
1034 let callback_clone = Arc::clone(&callback);
1035
1036 callback.on_step_start("step1", "Test", 1);
1037 callback_clone.on_step_complete("step1", "Output");
1038
1039 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1040 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 1);
1041 }
1042
1043 #[tokio::test]
1046 async fn test_chat_with_unicode_content() {
1047 let provider = MockProvider::new("你好,世界!🌍");
1048 let result = provider
1049 .chat("系统提示词", "用户消息 🎉", Some("gpt-4"))
1050 .await;
1051
1052 assert!(result.is_ok());
1053 assert_eq!(result.unwrap(), "你好,世界!🌍");
1054 }
1055
1056 #[tokio::test]
1057 async fn test_chat_with_long_content() {
1058 let long_response = "a".repeat(10000);
1059 let provider = MockProvider::new(&long_response);
1060 let result = provider.chat("System", "User", None).await;
1061
1062 assert!(result.is_ok());
1063 assert_eq!(result.unwrap().len(), 10000);
1064 }
1065
1066 #[test]
1067 fn test_callback_with_empty_strings() {
1068 let callback = MockCallback::new();
1069 callback.on_step_start("", "", 0);
1070 callback.on_step_complete("", "");
1071 callback.on_step_error("", "", false);
1072 callback.on_complete(false, Some(""));
1073
1074 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1075 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 1);
1076 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
1077 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
1078 }
1079
1080 use super::super::types::{SkillSource, WorkflowDefinition, WorkflowStep};
1083 use std::path::PathBuf;
1084
1085 fn create_test_skill(mode: SkillExecutionMode) -> SkillDefinition {
1087 SkillDefinition {
1088 skill_name: "test:test-skill".to_string(),
1089 display_name: "Test Skill".to_string(),
1090 description: "A test skill".to_string(),
1091 has_user_specified_description: true,
1092 markdown_content: "You are a helpful assistant.".to_string(),
1093 allowed_tools: Some(vec!["tool1".to_string(), "tool2".to_string()]),
1094 argument_hint: Some("input".to_string()),
1095 when_to_use: Some("When testing".to_string()),
1096 version: Some("1.0.0".to_string()),
1097 model: Some("gpt-4".to_string()),
1098 disable_model_invocation: false,
1099 user_invocable: true,
1100 source: SkillSource::User,
1101 base_dir: PathBuf::from("/test"),
1102 file_path: PathBuf::from("/test/SKILL.md"),
1103 supporting_files: vec![],
1104 execution_mode: mode,
1105 provider: None,
1106 workflow: None,
1107 }
1108 }
1109
1110 fn create_workflow_skill() -> SkillDefinition {
1112 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1113 skill.workflow = Some(WorkflowDefinition::new(vec![
1114 WorkflowStep::new("step1", "步骤一", "处理 ${user_input}", "result1"),
1115 WorkflowStep::new("step2", "步骤二", "继续 ${result1}", "result2")
1116 .with_dependency("step1"),
1117 ]));
1118 skill
1119 }
1120
1121 #[test]
1124 fn test_skill_executor_new() {
1125 let provider = MockProvider::new("response");
1126 let executor = SkillExecutor::new(provider);
1127
1128 let _provider_ref = executor.provider();
1130 }
1131
1132 #[test]
1133 fn test_skill_executor_provider_ref() {
1134 let provider = MockProvider::new("test response");
1135 let executor = SkillExecutor::new(provider);
1136
1137 let _provider_ref = executor.provider();
1139 }
1140
1141 #[tokio::test]
1144 async fn test_execute_dispatches_to_prompt_mode() {
1145 let provider = MockProvider::new("Prompt mode response");
1146 let executor = SkillExecutor::new(provider);
1147 let skill = create_test_skill(SkillExecutionMode::Prompt);
1148
1149 let result = executor.execute(&skill, "test input", None).await;
1150
1151 assert!(result.is_ok());
1152 let result = result.unwrap();
1153 assert!(result.success);
1154 assert_eq!(result.output, Some("Prompt mode response".to_string()));
1155 }
1156
1157 #[tokio::test]
1158 async fn test_execute_dispatches_to_workflow_mode_without_workflow() {
1159 let provider = MockProvider::new("response");
1160 let executor = SkillExecutor::new(provider);
1161 let skill = create_test_skill(SkillExecutionMode::Workflow);
1162
1163 let result = executor.execute(&skill, "test input", None).await;
1164
1165 assert!(result.is_err());
1167 let err = result.unwrap_err();
1168 assert!(err.is_invalid_config());
1169 assert!(err.message().contains("workflow"));
1170 }
1171
1172 #[tokio::test]
1173 async fn test_execute_dispatches_to_workflow_mode_with_workflow() {
1174 let provider = MockProvider::new("response");
1175 let executor = SkillExecutor::new(provider);
1176 let skill = create_workflow_skill();
1177
1178 let result = executor.execute(&skill, "test input", None).await;
1179
1180 assert!(result.is_ok());
1182 let result = result.unwrap();
1183 assert!(result.success);
1184 assert_eq!(result.steps_completed.len(), 2);
1186 assert!(result.output.is_some());
1188 }
1189
1190 #[tokio::test]
1191 async fn test_execute_returns_not_implemented_for_agent_mode() {
1192 let provider = MockProvider::new("response");
1193 let executor = SkillExecutor::new(provider);
1194 let skill = create_test_skill(SkillExecutionMode::Agent);
1195
1196 let result = executor.execute(&skill, "test input", None).await;
1197
1198 assert!(result.is_err());
1199 let err = result.unwrap_err();
1200 assert!(err.is_not_implemented());
1201 assert!(err.message().contains("Agent"));
1202 }
1203
1204 #[tokio::test]
1207 async fn test_prompt_mode_success() {
1208 let provider = MockProvider::new("LLM response");
1209 let executor = SkillExecutor::new(provider);
1210 let skill = create_test_skill(SkillExecutionMode::Prompt);
1211
1212 let result = executor.execute(&skill, "user input", None).await;
1213
1214 assert!(result.is_ok());
1215 let result = result.unwrap();
1216 assert!(result.success);
1217 assert_eq!(result.output, Some("LLM response".to_string()));
1218 assert!(result.error.is_none());
1219 assert_eq!(result.command_name, Some("test:test-skill".to_string()));
1220 assert_eq!(result.model, Some("gpt-4".to_string()));
1221 }
1222
1223 #[tokio::test]
1224 async fn test_prompt_mode_failure() {
1225 let provider = MockProvider::failing();
1226 let executor = SkillExecutor::new(provider);
1227 let skill = create_test_skill(SkillExecutionMode::Prompt);
1228
1229 let result = executor.execute(&skill, "user input", None).await;
1230
1231 assert!(result.is_ok()); let result = result.unwrap();
1233 assert!(!result.success);
1234 assert!(result.output.is_none());
1235 assert!(result.error.is_some());
1236 assert!(result.error.unwrap().contains("Provider"));
1237 }
1238
1239 #[tokio::test]
1240 async fn test_prompt_mode_with_callback() {
1241 let provider = MockProvider::new("response");
1242 let executor = SkillExecutor::new(provider);
1243 let skill = create_test_skill(SkillExecutionMode::Prompt);
1244 let callback = MockCallback::new();
1245
1246 let result = executor.execute(&skill, "input", Some(&callback)).await;
1247
1248 assert!(result.is_ok());
1249 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1250 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 1);
1251 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
1252 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 0);
1253 }
1254
1255 #[tokio::test]
1256 async fn test_prompt_mode_failure_with_callback() {
1257 let provider = MockProvider::failing();
1258 let executor = SkillExecutor::new(provider);
1259 let skill = create_test_skill(SkillExecutionMode::Prompt);
1260 let callback = MockCallback::new();
1261
1262 let result = executor.execute(&skill, "input", Some(&callback)).await;
1263
1264 assert!(result.is_ok());
1265 let result = result.unwrap();
1266 assert!(!result.success);
1267 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1268 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
1269 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
1270 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 0);
1271 }
1272
1273 #[tokio::test]
1274 async fn test_prompt_mode_without_callback() {
1275 let provider = MockProvider::new("response");
1276 let executor = SkillExecutor::new(provider);
1277 let skill = create_test_skill(SkillExecutionMode::Prompt);
1278
1279 let result = executor.execute(&skill, "input", None).await;
1281
1282 assert!(result.is_ok());
1283 assert!(result.unwrap().success);
1284 }
1285
1286 #[tokio::test]
1287 async fn test_prompt_mode_preserves_skill_metadata() {
1288 let provider = MockProvider::new("response");
1289 let executor = SkillExecutor::new(provider);
1290 let skill = create_test_skill(SkillExecutionMode::Prompt);
1291
1292 let result = executor.execute(&skill, "input", None).await.unwrap();
1293
1294 assert_eq!(result.command_name, Some("test:test-skill".to_string()));
1295 assert_eq!(
1296 result.allowed_tools,
1297 Some(vec!["tool1".to_string(), "tool2".to_string()])
1298 );
1299 assert_eq!(result.model, Some("gpt-4".to_string()));
1300 }
1301
1302 #[tokio::test]
1305 async fn test_workflow_mode_validates_workflow_definition() {
1306 let provider = MockProvider::new("response");
1307 let executor = SkillExecutor::new(provider);
1308 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1309 skill.workflow = None; let result = executor.execute(&skill, "input", None).await;
1312
1313 assert!(result.is_err());
1314 let err = result.unwrap_err();
1315 assert!(err.is_invalid_config());
1316 }
1317
1318 #[tokio::test]
1321 async fn test_execute_with_empty_input() {
1322 let provider = MockProvider::new("response");
1323 let executor = SkillExecutor::new(provider);
1324 let skill = create_test_skill(SkillExecutionMode::Prompt);
1325
1326 let result = executor.execute(&skill, "", None).await;
1327
1328 assert!(result.is_ok());
1329 assert!(result.unwrap().success);
1330 }
1331
1332 #[tokio::test]
1333 async fn test_execute_with_unicode_input() {
1334 let provider = MockProvider::new("你好,世界!");
1335 let executor = SkillExecutor::new(provider);
1336 let skill = create_test_skill(SkillExecutionMode::Prompt);
1337
1338 let result = executor.execute(&skill, "中文输入 🎉", None).await;
1339
1340 assert!(result.is_ok());
1341 let result = result.unwrap();
1342 assert!(result.success);
1343 assert_eq!(result.output, Some("你好,世界!".to_string()));
1344 }
1345
1346 #[tokio::test]
1347 async fn test_execute_with_long_input() {
1348 let long_input = "a".repeat(10000);
1349 let provider = MockProvider::new("response");
1350 let executor = SkillExecutor::new(provider);
1351 let skill = create_test_skill(SkillExecutionMode::Prompt);
1352
1353 let result = executor.execute(&skill, &long_input, None).await;
1354
1355 assert!(result.is_ok());
1356 assert!(result.unwrap().success);
1357 }
1358
1359 #[test]
1362 fn test_skill_executor_is_send_sync() {
1363 fn assert_send_sync<T: Send + Sync>() {}
1364 assert_send_sync::<SkillExecutor<MockProvider>>();
1365 }
1366
1367 struct RetryMockProvider {
1372 fail_count: std::sync::atomic::AtomicU32,
1374 fail_until: u32,
1376 success_response: String,
1378 }
1379
1380 impl RetryMockProvider {
1381 fn fail_then_succeed(fail_count: u32, response: &str) -> Self {
1383 Self {
1384 fail_count: std::sync::atomic::AtomicU32::new(0),
1385 fail_until: fail_count,
1386 success_response: response.to_string(),
1387 }
1388 }
1389
1390 fn always_fail() -> Self {
1392 Self {
1393 fail_count: std::sync::atomic::AtomicU32::new(0),
1394 fail_until: u32::MAX,
1395 success_response: String::new(),
1396 }
1397 }
1398
1399 fn call_count(&self) -> u32 {
1401 self.fail_count.load(Ordering::SeqCst)
1402 }
1403 }
1404
1405 #[async_trait]
1406 impl LlmProvider for RetryMockProvider {
1407 async fn chat(
1408 &self,
1409 _system_prompt: &str,
1410 _user_message: &str,
1411 _model: Option<&str>,
1412 ) -> Result<String, SkillError> {
1413 let current = self.fail_count.fetch_add(1, Ordering::SeqCst);
1414 if current < self.fail_until {
1415 Err(SkillError::provider_error(format!(
1416 "模拟失败 (第 {} 次调用)",
1417 current + 1
1418 )))
1419 } else {
1420 Ok(self.success_response.clone())
1421 }
1422 }
1423 }
1424
1425 struct RetryTrackingCallback {
1427 step_start_count: AtomicUsize,
1428 step_complete_count: AtomicUsize,
1429 step_error_count: AtomicUsize,
1430 will_retry_values: std::sync::Mutex<Vec<bool>>,
1432 }
1433
1434 impl RetryTrackingCallback {
1435 fn new() -> Self {
1436 Self {
1437 step_start_count: AtomicUsize::new(0),
1438 step_complete_count: AtomicUsize::new(0),
1439 step_error_count: AtomicUsize::new(0),
1440 will_retry_values: std::sync::Mutex::new(Vec::new()),
1441 }
1442 }
1443
1444 fn get_will_retry_values(&self) -> Vec<bool> {
1445 self.will_retry_values.lock().unwrap().clone()
1446 }
1447 }
1448
1449 impl ExecutionCallback for RetryTrackingCallback {
1450 fn on_step_start(&self, _step_id: &str, _step_name: &str, _total_steps: usize) {
1451 self.step_start_count.fetch_add(1, Ordering::SeqCst);
1452 }
1453
1454 fn on_step_complete(&self, _step_id: &str, _output: &str) {
1455 self.step_complete_count.fetch_add(1, Ordering::SeqCst);
1456 }
1457
1458 fn on_step_error(&self, _step_id: &str, _error: &str, will_retry: bool) {
1459 self.step_error_count.fetch_add(1, Ordering::SeqCst);
1460 self.will_retry_values.lock().unwrap().push(will_retry);
1461 }
1462
1463 fn on_complete(&self, _success: bool, _final_output: Option<&str>) {}
1464 }
1465
1466 #[tokio::test]
1469 async fn test_execute_step_with_retry_success_first_try() {
1470 let provider = MockProvider::new("成功响应");
1471 let executor = SkillExecutor::new(provider);
1472 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1473 let callback = RetryTrackingCallback::new();
1474
1475 let result = executor
1476 .execute_step_with_retry(&step, "测试提示", 2, 1, &callback)
1477 .await;
1478
1479 assert!(result.is_ok());
1480 assert_eq!(result.unwrap(), "成功响应");
1481 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1483 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 0);
1484 }
1485
1486 #[tokio::test]
1487 async fn test_execute_step_with_retry_success_after_one_retry() {
1488 let provider = RetryMockProvider::fail_then_succeed(1, "重试后成功");
1490 let executor = SkillExecutor::new(provider);
1491 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1492 let callback = RetryTrackingCallback::new();
1493
1494 let result = executor
1495 .execute_step_with_retry(&step, "测试提示", 2, 1, &callback)
1496 .await;
1497
1498 assert!(result.is_ok());
1499 assert_eq!(result.unwrap(), "重试后成功");
1500 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
1502 let will_retry_values = callback.get_will_retry_values();
1503 assert_eq!(will_retry_values, vec![true]);
1504 }
1505
1506 #[tokio::test]
1507 async fn test_execute_step_with_retry_success_after_two_retries() {
1508 let provider = RetryMockProvider::fail_then_succeed(2, "两次重试后成功");
1510 let executor = SkillExecutor::new(provider);
1511 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1512 let callback = RetryTrackingCallback::new();
1513
1514 let result = executor
1515 .execute_step_with_retry(&step, "测试提示", 2, 1, &callback)
1516 .await;
1517
1518 assert!(result.is_ok());
1519 assert_eq!(result.unwrap(), "两次重试后成功");
1520 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 2);
1522 let will_retry_values = callback.get_will_retry_values();
1523 assert_eq!(will_retry_values, vec![true, true]);
1524 }
1525
1526 #[tokio::test]
1527 async fn test_execute_step_with_retry_all_retries_exhausted() {
1528 let provider = RetryMockProvider::always_fail();
1530 let executor = SkillExecutor::new(provider);
1531 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1532 let callback = RetryTrackingCallback::new();
1533
1534 let result = executor
1535 .execute_step_with_retry(&step, "测试提示", 2, 1, &callback)
1536 .await;
1537
1538 assert!(result.is_err());
1539 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 3);
1542 let will_retry_values = callback.get_will_retry_values();
1543 assert_eq!(will_retry_values, vec![true, true, false]);
1544 }
1545
1546 #[tokio::test]
1547 async fn test_execute_step_with_retry_zero_retries() {
1548 let provider = RetryMockProvider::always_fail();
1550 let executor = SkillExecutor::new(provider);
1551 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1552 let callback = RetryTrackingCallback::new();
1553
1554 let result = executor
1555 .execute_step_with_retry(&step, "测试提示", 0, 1, &callback)
1556 .await;
1557
1558 assert!(result.is_err());
1559 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 1);
1561 let will_retry_values = callback.get_will_retry_values();
1562 assert_eq!(will_retry_values, vec![false]);
1563 }
1564
1565 #[tokio::test]
1568 async fn test_execute_step_with_retry_calls_on_step_start() {
1569 let provider = MockProvider::new("响应");
1570 let executor = SkillExecutor::new(provider);
1571 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1572 let callback = RetryTrackingCallback::new();
1573
1574 let _ = executor
1575 .execute_step_with_retry(&step, "测试提示", 2, 5, &callback)
1576 .await;
1577
1578 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 1);
1580 }
1581
1582 #[tokio::test]
1583 async fn test_execute_step_with_retry_will_retry_flag_correct() {
1584 let provider = RetryMockProvider::always_fail();
1587 let executor = SkillExecutor::new(provider);
1588 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1589 let callback = RetryTrackingCallback::new();
1590
1591 let _ = executor
1592 .execute_step_with_retry(&step, "测试提示", 3, 1, &callback)
1593 .await;
1594
1595 let will_retry_values = callback.get_will_retry_values();
1598 assert_eq!(will_retry_values, vec![true, true, true, false]);
1599 }
1600
1601 #[tokio::test]
1604 async fn test_execute_step_with_retry_provider_call_count() {
1605 let provider = RetryMockProvider::always_fail();
1607 let executor = SkillExecutor::new(provider);
1608 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1609 let callback = NoopCallback;
1610
1611 let _ = executor
1612 .execute_step_with_retry(&step, "测试提示", 2, 1, &callback)
1613 .await;
1614
1615 assert_eq!(executor.provider().call_count(), 3);
1617 }
1618
1619 #[tokio::test]
1620 async fn test_execute_step_with_retry_stops_on_success() {
1621 let provider = RetryMockProvider::fail_then_succeed(1, "成功");
1623 let executor = SkillExecutor::new(provider);
1624 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1625 let callback = NoopCallback;
1626
1627 let _ = executor
1628 .execute_step_with_retry(&step, "测试提示", 5, 1, &callback)
1629 .await;
1630
1631 assert_eq!(executor.provider().call_count(), 2);
1633 }
1634
1635 #[tokio::test]
1638 async fn test_execute_step_with_retry_returns_last_error() {
1639 let provider = RetryMockProvider::always_fail();
1640 let executor = SkillExecutor::new(provider);
1641 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1642 let callback = NoopCallback;
1643
1644 let result = executor
1645 .execute_step_with_retry(&step, "测试提示", 1, 1, &callback)
1646 .await;
1647
1648 assert!(result.is_err());
1649 let err = result.unwrap_err();
1650 assert!(err.is_provider_error());
1652 }
1653
1654 #[tokio::test]
1657 async fn test_execute_step_with_retry_empty_prompt() {
1658 let provider = MockProvider::new("响应");
1659 let executor = SkillExecutor::new(provider);
1660 let step = WorkflowStep::new("step1", "测试步骤", "", "output");
1661 let callback = NoopCallback;
1662
1663 let result = executor
1664 .execute_step_with_retry(&step, "", 2, 1, &callback)
1665 .await;
1666
1667 assert!(result.is_ok());
1668 }
1669
1670 #[tokio::test]
1671 async fn test_execute_step_with_retry_unicode_content() {
1672 let provider = MockProvider::new("中文响应 🎉");
1673 let executor = SkillExecutor::new(provider);
1674 let step = WorkflowStep::new("步骤1", "中文步骤名", "中文提示", "输出");
1675 let callback = NoopCallback;
1676
1677 let result = executor
1678 .execute_step_with_retry(&step, "中文提示 🚀", 2, 1, &callback)
1679 .await;
1680
1681 assert!(result.is_ok());
1682 assert_eq!(result.unwrap(), "中文响应 🎉");
1683 }
1684
1685 #[tokio::test]
1686 async fn test_execute_step_with_retry_large_max_retries() {
1687 let provider = RetryMockProvider::fail_then_succeed(5, "成功");
1689 let executor = SkillExecutor::new(provider);
1690 let step = WorkflowStep::new("step1", "测试步骤", "测试提示", "output");
1691 let callback = NoopCallback;
1692
1693 let result = executor
1694 .execute_step_with_retry(&step, "测试提示", 10, 1, &callback)
1695 .await;
1696
1697 assert!(result.is_ok());
1698 assert_eq!(executor.provider().call_count(), 6);
1700 }
1701
1702 #[tokio::test]
1707 async fn test_workflow_mode_single_step_success() {
1708 let provider = MockProvider::new("步骤输出");
1709 let executor = SkillExecutor::new(provider);
1710 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1711 skill.workflow = Some(WorkflowDefinition::new(vec![WorkflowStep::new(
1712 "step1",
1713 "唯一步骤",
1714 "处理 ${user_input}",
1715 "result",
1716 )]));
1717
1718 let result = executor.execute(&skill, "用户输入", None).await;
1719
1720 assert!(result.is_ok());
1721 let result = result.unwrap();
1722 assert!(result.success);
1723 assert_eq!(result.output, Some("步骤输出".to_string()));
1724 assert_eq!(result.steps_completed.len(), 1);
1725 assert_eq!(result.steps_completed[0].step_id, "step1");
1726 assert!(result.steps_completed[0].success);
1727 }
1728
1729 #[tokio::test]
1730 async fn test_workflow_mode_multiple_steps_success() {
1731 let provider = MockProvider::new("响应");
1732 let executor = SkillExecutor::new(provider);
1733 let skill = create_workflow_skill();
1734
1735 let result = executor.execute(&skill, "测试输入", None).await;
1736
1737 assert!(result.is_ok());
1738 let result = result.unwrap();
1739 assert!(result.success);
1740 assert_eq!(result.steps_completed.len(), 2);
1741 assert_eq!(result.steps_completed[0].step_id, "step1");
1743 assert_eq!(result.steps_completed[1].step_id, "step2");
1744 }
1745
1746 #[tokio::test]
1747 async fn test_workflow_mode_with_callback() {
1748 let provider = MockProvider::new("响应");
1749 let executor = SkillExecutor::new(provider);
1750 let skill = create_workflow_skill();
1751 let callback = MockCallback::new();
1752
1753 let result = executor.execute(&skill, "输入", Some(&callback)).await;
1754
1755 assert!(result.is_ok());
1756 assert_eq!(callback.step_start_count.load(Ordering::SeqCst), 2);
1758 assert_eq!(callback.step_complete_count.load(Ordering::SeqCst), 2);
1760 assert_eq!(callback.complete_count.load(Ordering::SeqCst), 1);
1762 assert_eq!(callback.step_error_count.load(Ordering::SeqCst), 0);
1764 }
1765
1766 struct PromptRecordingProvider {
1770 prompts: std::sync::Mutex<Vec<String>>,
1771 response: String,
1772 }
1773
1774 impl PromptRecordingProvider {
1775 fn new(response: &str) -> Self {
1776 Self {
1777 prompts: std::sync::Mutex::new(Vec::new()),
1778 response: response.to_string(),
1779 }
1780 }
1781
1782 fn get_prompts(&self) -> Vec<String> {
1783 self.prompts.lock().unwrap().clone()
1784 }
1785 }
1786
1787 #[async_trait]
1788 impl LlmProvider for PromptRecordingProvider {
1789 async fn chat(
1790 &self,
1791 _system_prompt: &str,
1792 user_message: &str,
1793 _model: Option<&str>,
1794 ) -> Result<String, SkillError> {
1795 self.prompts.lock().unwrap().push(user_message.to_string());
1796 Ok(self.response.clone())
1797 }
1798 }
1799
1800 #[tokio::test]
1801 async fn test_workflow_mode_variable_interpolation_user_input() {
1802 let provider = PromptRecordingProvider::new("响应");
1803 let executor = SkillExecutor::new(provider);
1804 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1805 skill.workflow = Some(WorkflowDefinition::new(vec![WorkflowStep::new(
1806 "step1",
1807 "步骤一",
1808 "处理用户输入: ${user_input}",
1809 "result",
1810 )]));
1811
1812 let _ = executor.execute(&skill, "Hello World", None).await;
1813
1814 let prompts = executor.provider().get_prompts();
1815 assert_eq!(prompts.len(), 1);
1816 assert_eq!(prompts[0], "处理用户输入: Hello World");
1817 }
1818
1819 #[tokio::test]
1820 async fn test_workflow_mode_variable_interpolation_step_output() {
1821 let provider = PromptRecordingProvider::new("步骤输出");
1822 let executor = SkillExecutor::new(provider);
1823 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1824 skill.workflow = Some(WorkflowDefinition::new(vec![
1825 WorkflowStep::new("step1", "步骤一", "第一步", "result1"),
1826 WorkflowStep::new("step2", "步骤二", "基于 ${result1} 继续", "result2")
1827 .with_dependency("step1"),
1828 ]));
1829
1830 let _ = executor.execute(&skill, "输入", None).await;
1831
1832 let prompts = executor.provider().get_prompts();
1833 assert_eq!(prompts.len(), 2);
1834 assert_eq!(prompts[0], "第一步");
1835 assert_eq!(prompts[1], "基于 步骤输出 继续");
1837 }
1838
1839 #[tokio::test]
1842 async fn test_workflow_mode_continue_on_failure_false_aborts() {
1843 let provider = RetryMockProvider::always_fail();
1844 let executor = SkillExecutor::new(provider);
1845 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1846 let mut workflow = WorkflowDefinition::new(vec![
1847 WorkflowStep::new("step1", "步骤一", "第一步", "result1"),
1848 WorkflowStep::new("step2", "步骤二", "第二步", "result2").with_dependency("step1"),
1849 ]);
1850 workflow.continue_on_failure = false;
1851 workflow.max_retries = 0; skill.workflow = Some(workflow);
1853
1854 let result = executor.execute(&skill, "输入", None).await;
1855
1856 assert!(result.is_ok());
1857 let result = result.unwrap();
1858 assert!(!result.success);
1859 assert_eq!(result.steps_completed.len(), 1);
1861 assert!(!result.steps_completed[0].success);
1862 assert!(result.error.unwrap().contains("step1"));
1864 }
1865
1866 #[tokio::test]
1867 async fn test_workflow_mode_continue_on_failure_true_continues() {
1868 let provider = RetryMockProvider::always_fail();
1869 let executor = SkillExecutor::new(provider);
1870 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1871 let mut workflow = WorkflowDefinition::new(vec![
1872 WorkflowStep::new("step1", "步骤一", "第一步", "result1"),
1873 WorkflowStep::new("step2", "步骤二", "第二步", "result2"),
1874 ]);
1875 workflow.continue_on_failure = true;
1876 workflow.max_retries = 0; skill.workflow = Some(workflow);
1878
1879 let result = executor.execute(&skill, "输入", None).await;
1880
1881 assert!(result.is_ok());
1882 let result = result.unwrap();
1883 assert!(!result.success); assert_eq!(result.steps_completed.len(), 2);
1887 assert!(!result.steps_completed[0].success);
1888 assert!(!result.steps_completed[1].success);
1889 }
1890
1891 #[tokio::test]
1894 async fn test_workflow_mode_cyclic_dependency_error() {
1895 let provider = MockProvider::new("响应");
1896 let executor = SkillExecutor::new(provider);
1897 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1898 skill.workflow = Some(WorkflowDefinition::new(vec![
1899 WorkflowStep::new("step1", "步骤一", "第一步", "result1").with_dependency("step2"),
1900 WorkflowStep::new("step2", "步骤二", "第二步", "result2").with_dependency("step1"),
1901 ]));
1902
1903 let result = executor.execute(&skill, "输入", None).await;
1904
1905 assert!(result.is_err());
1906 let err = result.unwrap_err();
1907 assert!(err.is_cyclic_dependency());
1908 }
1909
1910 #[tokio::test]
1911 async fn test_workflow_mode_missing_dependency_error() {
1912 let provider = MockProvider::new("响应");
1913 let executor = SkillExecutor::new(provider);
1914 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1915 skill.workflow = Some(WorkflowDefinition::new(vec![WorkflowStep::new(
1916 "step1",
1917 "步骤一",
1918 "第一步",
1919 "result1",
1920 )
1921 .with_dependency("nonexistent")]));
1922
1923 let result = executor.execute(&skill, "输入", None).await;
1924
1925 assert!(result.is_err());
1926 let err = result.unwrap_err();
1927 assert!(err.is_missing_dependency());
1928 }
1929
1930 #[tokio::test]
1933 async fn test_workflow_mode_empty_steps() {
1934 let provider = MockProvider::new("响应");
1935 let executor = SkillExecutor::new(provider);
1936 let mut skill = create_test_skill(SkillExecutionMode::Workflow);
1937 skill.workflow = Some(WorkflowDefinition::new(vec![]));
1938
1939 let result = executor.execute(&skill, "输入", None).await;
1940
1941 assert!(result.is_ok());
1942 let result = result.unwrap();
1943 assert!(result.success);
1944 assert_eq!(result.steps_completed.len(), 0);
1945 assert!(result.output.is_none()); }
1947
1948 #[tokio::test]
1951 async fn test_workflow_mode_preserves_skill_metadata() {
1952 let provider = MockProvider::new("响应");
1953 let executor = SkillExecutor::new(provider);
1954 let skill = create_workflow_skill();
1955
1956 let result = executor.execute(&skill, "输入", None).await.unwrap();
1957
1958 assert_eq!(result.command_name, Some("test:test-skill".to_string()));
1959 assert_eq!(
1960 result.allowed_tools,
1961 Some(vec!["tool1".to_string(), "tool2".to_string()])
1962 );
1963 assert_eq!(result.model, Some("gpt-4".to_string()));
1964 }
1965}