Files
Genarrative/server-rs/crates/shared-contracts/src/external_generation.rs
T
kdletters e1d031b86f 增加外部API的MCP与异步生成模式
新增托管式Streamable HTTP MCP端点并复用外部API Key鉴权
统一图片视频音频等生成请求为幂等异步提交和状态查询
提供可发现的使用说明、OpenAPI资源及完整Skill下载包
同步更新OpenAPI契约并规定接口变更必须连带维护
适配AI游戏创作Shell的异步提交轮询与安全重试
补齐MCP方法映射、异步Worker和客户端回归测试
2026-07-31 19:48:28 +08:00

160 lines
5.2 KiB
Rust

use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ExternalGenerationJobStatus {
Queued,
Running,
Completed,
Failed,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationQueueOverview {
pub pending_count: u32,
pub running_count: u32,
pub unacknowledged_terminal_count: u32,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationQueueOverviewResponse {
pub overview: ExternalGenerationQueueOverview,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationJobStatusRecord {
pub operation_id: String,
pub status: ExternalGenerationJobStatus,
pub phase_label: String,
pub phase_detail: String,
pub progress: u8,
pub error: Option<String>,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationJobStatusDetailRecord {
#[serde(flatten)]
pub status: ExternalGenerationJobStatusRecord,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub warning: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationJobStatusResponse {
pub job: ExternalGenerationJobStatusDetailRecord,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalEditorGenerationSubmissionResponse {
pub operation_id: String,
pub kind: String,
pub status: ExternalGenerationJobStatus,
pub status_url: String,
pub poll_after_ms: u64,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalEditorGenerationJobResponse {
pub operation_id: String,
pub kind: String,
pub status: ExternalGenerationJobStatus,
pub phase_label: String,
pub phase_detail: String,
pub progress: u8,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub warning: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub poll_after_ms: Option<u64>,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationTaskRecord {
pub job_id: String,
pub job_kind: String,
pub source_module: String,
pub source_entity_id: String,
pub request_label: String,
pub request_prompt: Option<String>,
pub status: ExternalGenerationJobStatus,
pub phase_label: String,
pub phase_detail: String,
pub progress: u8,
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub warning: Option<String>,
pub price_mud_points: u64,
pub refund_ledger_id: Option<String>,
pub notification_acknowledged_at: Option<String>,
pub created_at: String,
pub started_at: Option<String>,
pub completed_at: Option<String>,
pub updated_at: String,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationTaskListResponse {
pub tasks: Vec<ExternalGenerationTaskRecord>,
pub overview: ExternalGenerationQueueOverview,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationTaskAcknowledgeRequest {
pub job_ids: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalGenerationTaskAcknowledgeResponse {
pub acknowledged_tasks: Vec<ExternalGenerationTaskRecord>,
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn status_detail_serializes_warning_without_reusing_error() {
let payload = serde_json::to_value(ExternalGenerationJobStatusResponse {
job: ExternalGenerationJobStatusDetailRecord {
status: ExternalGenerationJobStatusRecord {
operation_id: "job-1".to_string(),
status: ExternalGenerationJobStatus::Completed,
phase_label: "图标素材生成".to_string(),
phase_detail: "生成已完成。".to_string(),
progress: 100,
error: None,
updated_at_micros: 1,
},
warning: Some("连通域数量不足".to_string()),
},
})
.expect("状态响应应可序列化");
assert_eq!(payload["job"]["warning"], json!("连通域数量不足"));
assert_eq!(payload["job"]["error"], json!(null));
assert_eq!(payload["job"]["status"], json!("completed"));
}
}