224 lines
7.4 KiB
Rust
224 lines
7.4 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CreateAiTaskRequest {
|
|
pub task_kind: String,
|
|
pub request_label: String,
|
|
pub source_module: String,
|
|
#[serde(default)]
|
|
pub source_entity_id: Option<String>,
|
|
#[serde(default)]
|
|
pub request_payload_json: Option<String>,
|
|
#[serde(default)]
|
|
pub stage_kinds: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AppendAiTextChunkRequest {
|
|
pub stage_kind: String,
|
|
pub sequence: u32,
|
|
pub delta_text: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CompleteAiStageRequest {
|
|
#[serde(default)]
|
|
pub text_output: Option<String>,
|
|
#[serde(default)]
|
|
pub structured_payload_json: Option<String>,
|
|
#[serde(default)]
|
|
pub warning_messages: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AttachAiResultReferenceRequest {
|
|
pub reference_kind: String,
|
|
pub reference_id: String,
|
|
#[serde(default)]
|
|
pub label: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct FailAiTaskRequest {
|
|
pub failure_message: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiTaskStagePayload {
|
|
pub stage_kind: String,
|
|
pub label: String,
|
|
pub detail: String,
|
|
pub order: u32,
|
|
pub status: String,
|
|
#[serde(default)]
|
|
pub text_output: Option<String>,
|
|
#[serde(default)]
|
|
pub structured_payload_json: Option<String>,
|
|
pub warning_messages: Vec<String>,
|
|
#[serde(default)]
|
|
pub started_at: Option<String>,
|
|
#[serde(default)]
|
|
pub completed_at: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiResultReferencePayload {
|
|
pub result_ref_id: String,
|
|
pub task_id: String,
|
|
pub reference_kind: String,
|
|
pub reference_id: String,
|
|
#[serde(default)]
|
|
pub label: Option<String>,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiTextChunkPayload {
|
|
pub chunk_id: String,
|
|
pub task_id: String,
|
|
pub stage_kind: String,
|
|
pub sequence: u32,
|
|
pub delta_text: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiTaskPayload {
|
|
pub task_id: String,
|
|
pub task_kind: String,
|
|
pub owner_user_id: String,
|
|
pub request_label: String,
|
|
pub source_module: String,
|
|
#[serde(default)]
|
|
pub source_entity_id: Option<String>,
|
|
#[serde(default)]
|
|
pub request_payload_json: Option<String>,
|
|
pub status: String,
|
|
#[serde(default)]
|
|
pub failure_message: Option<String>,
|
|
pub stages: Vec<AiTaskStagePayload>,
|
|
pub result_references: Vec<AiResultReferencePayload>,
|
|
#[serde(default)]
|
|
pub latest_text_output: Option<String>,
|
|
#[serde(default)]
|
|
pub latest_structured_payload_json: Option<String>,
|
|
pub version: u32,
|
|
pub created_at: String,
|
|
#[serde(default)]
|
|
pub started_at: Option<String>,
|
|
#[serde(default)]
|
|
pub completed_at: Option<String>,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiTaskMutationResponse {
|
|
pub ai_task: AiTaskPayload,
|
|
#[serde(default)]
|
|
pub ai_text_chunk: Option<AiTextChunkPayload>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct AiTaskAcceptedResponse {
|
|
pub accepted: bool,
|
|
pub task_id: String,
|
|
pub action: String,
|
|
#[serde(default)]
|
|
pub stage_kind: Option<String>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn create_ai_task_request_uses_camel_case_fields() {
|
|
let payload = serde_json::to_value(CreateAiTaskRequest {
|
|
task_kind: "story_generation".to_string(),
|
|
request_label: "营地开场".to_string(),
|
|
source_module: "story".to_string(),
|
|
source_entity_id: Some("storysess_001".to_string()),
|
|
request_payload_json: Some("{\"scene\":\"camp\"}".to_string()),
|
|
stage_kinds: vec!["prepare_prompt".to_string(), "request_model".to_string()],
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(
|
|
payload,
|
|
json!({
|
|
"taskKind": "story_generation",
|
|
"requestLabel": "营地开场",
|
|
"sourceModule": "story",
|
|
"sourceEntityId": "storysess_001",
|
|
"requestPayloadJson": "{\"scene\":\"camp\"}",
|
|
"stageKinds": ["prepare_prompt", "request_model"]
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ai_task_mutation_response_uses_camel_case_fields() {
|
|
let payload = serde_json::to_value(AiTaskMutationResponse {
|
|
ai_task: AiTaskPayload {
|
|
task_id: "aitask_001".to_string(),
|
|
task_kind: "npc_chat".to_string(),
|
|
owner_user_id: "user_001".to_string(),
|
|
request_label: "试探问话".to_string(),
|
|
source_module: "npc".to_string(),
|
|
source_entity_id: Some("npc_001".to_string()),
|
|
request_payload_json: None,
|
|
status: "running".to_string(),
|
|
failure_message: None,
|
|
stages: vec![AiTaskStagePayload {
|
|
stage_kind: "request_model".to_string(),
|
|
label: "请求模型".to_string(),
|
|
detail: "向上游模型发起正式推理请求。".to_string(),
|
|
order: 1,
|
|
status: "running".to_string(),
|
|
text_output: Some("你盯着对方的刀柄。".to_string()),
|
|
structured_payload_json: None,
|
|
warning_messages: vec![],
|
|
started_at: Some("2026-04-22T12:00:00Z".to_string()),
|
|
completed_at: None,
|
|
}],
|
|
result_references: vec![],
|
|
latest_text_output: Some("你盯着对方的刀柄。".to_string()),
|
|
latest_structured_payload_json: None,
|
|
version: 2,
|
|
created_at: "2026-04-22T12:00:00Z".to_string(),
|
|
started_at: Some("2026-04-22T12:00:01Z".to_string()),
|
|
completed_at: None,
|
|
updated_at: "2026-04-22T12:00:02Z".to_string(),
|
|
},
|
|
ai_text_chunk: Some(AiTextChunkPayload {
|
|
chunk_id: "aichunk_001".to_string(),
|
|
task_id: "aitask_001".to_string(),
|
|
stage_kind: "request_model".to_string(),
|
|
sequence: 1,
|
|
delta_text: "你".to_string(),
|
|
created_at: "2026-04-22T12:00:02Z".to_string(),
|
|
}),
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(payload["aiTask"]["taskId"], json!("aitask_001"));
|
|
assert_eq!(
|
|
payload["aiTask"]["stages"][0]["stageKind"],
|
|
json!("request_model")
|
|
);
|
|
assert_eq!(payload["aiTextChunk"]["deltaText"], json!("你"));
|
|
}
|
|
}
|