This commit is contained in:
2026-05-08 11:44:42 +08:00
parent b08127031c
commit abf1f1ebea
249 changed files with 39411 additions and 887 deletions

View File

@@ -0,0 +1,112 @@
use async_trait::async_trait;
use crate::{
callbacks::CreativeAgentCallbacks, error::PlatformAgentError,
function_agent::FunctionAgentLimits,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzlePhase1AgentInput {
pub session_id: String,
pub user_text: String,
pub image_urls: Vec<String>,
pub limits: FunctionAgentLimits,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzlePhase1AgentOutput {
pub session_id: String,
pub model: String,
pub final_message: String,
pub tool_call_count: usize,
}
#[async_trait]
pub trait CreativeAgentExecutor: Send + Sync {
async fn run_puzzle_phase1(
&self,
input: PuzzlePhase1AgentInput,
callbacks: CreativeAgentCallbacks,
) -> Result<PuzzlePhase1AgentOutput, PlatformAgentError>;
}
#[derive(Clone, Debug, Default)]
pub struct MockLangChainRustAgentExecutor;
#[async_trait]
impl CreativeAgentExecutor for MockLangChainRustAgentExecutor {
async fn run_puzzle_phase1(
&self,
input: PuzzlePhase1AgentInput,
callbacks: CreativeAgentCallbacks,
) -> Result<PuzzlePhase1AgentOutput, PlatformAgentError> {
input.limits.validate()?;
if input.session_id.trim().is_empty() {
return Err(PlatformAgentError::InvalidInput(
"creative session_id 不能为空".to_string(),
));
}
if input.user_text.trim().is_empty() && input.image_urls.is_empty() {
return Err(PlatformAgentError::InvalidInput(
"创意 Agent 输入文本和图片不能同时为空".to_string(),
));
}
callbacks.stage("perceiving");
callbacks.stage("thinking");
callbacks.stage("remembering");
callbacks.stage("selecting_puzzle_template");
callbacks.stage("waiting_template_confirmation");
Ok(PuzzlePhase1AgentOutput {
session_id: input.session_id,
model: crate::apimart_gpt5_adapter::CREATIVE_AGENT_GPT5_MODEL.to_string(),
final_message: "已完成创意 Agent Phase 1 mock 执行,可进入模板确认。".to_string(),
tool_call_count: 0,
})
}
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use super::*;
#[tokio::test]
async fn mock_executor_keeps_gpt5_model_and_emits_stages() {
let events = Arc::new(Mutex::new(Vec::new()));
let events_clone = events.clone();
let callbacks = CreativeAgentCallbacks::new(move |event| {
events_clone.lock().expect("events lock").push(event.label);
});
let output = MockLangChainRustAgentExecutor
.run_puzzle_phase1(
PuzzlePhase1AgentInput {
session_id: "creative-session-test".to_string(),
user_text: "做一个家庭纪念拼图".to_string(),
image_urls: vec!["https://example.com/ref.png".to_string()],
limits: FunctionAgentLimits::default(),
},
callbacks,
)
.await
.expect("mock executor should succeed");
assert_eq!(
output.model,
crate::apimart_gpt5_adapter::CREATIVE_AGENT_GPT5_MODEL
);
assert_eq!(
events.lock().expect("events lock").as_slice(),
[
"perceiving",
"thinking",
"remembering",
"selecting_puzzle_template",
"waiting_template_confirmation",
]
);
}
}