- refactor(frontend): replace string literals with WorkflowEventType enum for event handling - feat(backend): export WorkflowEventType in common-contracts and openapi - fix(tests): update end-to-end tests to match new ContextSelectorConfig and LlmConfig types - chore: regenerate openapi.json and frontend client schemas
308 lines
8.6 KiB
Rust
308 lines
8.6 KiB
Rust
use uuid::Uuid;
|
||
use crate::symbol_utils::CanonicalSymbol;
|
||
use crate::subjects::{NatsSubject, SubjectMessage};
|
||
use std::collections::HashMap;
|
||
use service_kit::api_dto;
|
||
use crate::configs::LlmConfig;
|
||
|
||
// --- Commands ---
|
||
|
||
// Topic: workflow.commands.start
|
||
/// Command to initiate a new workflow.
|
||
/// Published by: `api-gateway`
|
||
/// Consumed by: `workflow-orchestrator`
|
||
#[api_dto]
|
||
pub struct StartWorkflowCommand {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub market: String,
|
||
pub template_id: String,
|
||
}
|
||
|
||
impl SubjectMessage for StartWorkflowCommand {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::WorkflowCommandStart
|
||
}
|
||
}
|
||
|
||
// Topic: workflow.commands.sync_state
|
||
/// Command to request a state snapshot for re-alignment.
|
||
/// Published by: `api-gateway` (on client connect/reconnect)
|
||
/// Consumed by: `workflow-orchestrator`
|
||
#[api_dto]
|
||
pub struct SyncStateCommand {
|
||
pub request_id: Uuid,
|
||
}
|
||
|
||
impl SubjectMessage for SyncStateCommand {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::WorkflowCommandSyncState
|
||
}
|
||
}
|
||
|
||
/// Command to trigger data fetching.
|
||
/// Published by: `workflow-orchestrator` (previously api-gateway)
|
||
/// Consumed by: `*-provider-services`
|
||
#[api_dto]
|
||
pub struct FetchCompanyDataCommand {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub market: String,
|
||
pub template_id: Option<String>, // Optional trigger for analysis
|
||
pub output_path: Option<String>, // New: Unified I/O Binding
|
||
}
|
||
|
||
impl SubjectMessage for FetchCompanyDataCommand {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::DataFetchCommands
|
||
}
|
||
}
|
||
|
||
/// Command to start a full report generation workflow.
|
||
/// Published by: `workflow-orchestrator` (previously api-gateway)
|
||
/// Consumed by: `report-generator-service`
|
||
#[api_dto]
|
||
pub struct GenerateReportCommand {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub template_id: String,
|
||
/// The task_id in the workflow DAG that triggered this.
|
||
/// Used for reporting progress/content back to the specific node.
|
||
pub task_id: Option<String>,
|
||
pub module_id: Option<String>,
|
||
|
||
// --- New Fields for Refactored Context Mechanism ---
|
||
pub commit_hash: Option<String>,
|
||
pub input_bindings: Option<Vec<String>>, // Resolved physical paths
|
||
pub output_path: Option<String>,
|
||
pub llm_config: Option<LlmConfig>,
|
||
pub analysis_prompt: Option<String>,
|
||
}
|
||
|
||
impl SubjectMessage for GenerateReportCommand {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::AnalysisCommandGenerateReport
|
||
}
|
||
}
|
||
|
||
// --- Events ---
|
||
|
||
/// Metadata produced by a task execution.
|
||
#[api_dto]
|
||
pub struct TaskMetadata {
|
||
/// The primary output file path (e.g. analysis report)
|
||
pub output_path: Option<String>,
|
||
/// The execution trace log path
|
||
pub execution_log_path: Option<String>,
|
||
/// Additional arbitrary metadata
|
||
pub extra: HashMap<String, serde_json::Value>,
|
||
}
|
||
|
||
/// Comprehensive snapshot state for a single task
|
||
#[api_dto]
|
||
pub struct TaskStateSnapshot {
|
||
pub task_id: String,
|
||
pub status: TaskStatus,
|
||
pub logs: Vec<String>, // Historical logs for this task
|
||
pub content: Option<String>, // Current streamed content buffer
|
||
pub input_commit: Option<String>,
|
||
pub output_commit: Option<String>,
|
||
pub metadata: Option<TaskMetadata>,
|
||
}
|
||
|
||
#[api_dto]
|
||
#[derive(Copy, PartialEq, Eq, Hash)]
|
||
pub enum WorkflowEventType {
|
||
WorkflowStarted,
|
||
TaskStateChanged,
|
||
TaskStreamUpdate,
|
||
TaskLog,
|
||
WorkflowCompleted,
|
||
WorkflowFailed,
|
||
WorkflowStateSnapshot,
|
||
}
|
||
|
||
// Topic: events.workflow.{request_id}
|
||
/// Unified event stream for frontend consumption.
|
||
#[api_dto]
|
||
#[serde(tag = "type", content = "payload")]
|
||
pub enum WorkflowEvent {
|
||
// 1. 流程初始化 (携带完整的任务依赖图)
|
||
WorkflowStarted {
|
||
timestamp: i64,
|
||
// 定义所有任务及其依赖关系,前端可据此绘制流程图或进度条
|
||
task_graph: WorkflowDag
|
||
},
|
||
|
||
// 2. 任务状态变更 (核心事件)
|
||
TaskStateChanged {
|
||
task_id: String, // e.g., "fetch:tushare", "process:clean_financials", "module:swot_analysis"
|
||
task_type: TaskType, // DataFetch | DataProcessing | Analysis
|
||
status: TaskStatus, // Pending, Scheduled, Running, Completed, Failed, Skipped
|
||
message: Option<String>,
|
||
timestamp: i64,
|
||
progress: Option<u8>, // 0-100
|
||
// New fields for Context Inspector
|
||
input_commit: Option<String>,
|
||
output_commit: Option<String>,
|
||
},
|
||
|
||
// 3. 任务流式输出 (用于 LLM 打字机效果)
|
||
TaskStreamUpdate {
|
||
task_id: String,
|
||
content_delta: String,
|
||
index: u32
|
||
},
|
||
|
||
// 3.5. 任务日志 (用于实时展示详细执行过程)
|
||
TaskLog {
|
||
task_id: String,
|
||
level: String, // INFO, WARN, ERROR
|
||
message: String,
|
||
timestamp: i64,
|
||
},
|
||
|
||
// 4. 流程整体结束
|
||
WorkflowCompleted {
|
||
result_summary: Option<serde_json::Value>,
|
||
end_timestamp: i64
|
||
},
|
||
|
||
WorkflowFailed {
|
||
reason: String,
|
||
is_fatal: bool,
|
||
end_timestamp: i64
|
||
},
|
||
|
||
// 5. 状态快照 (用于重连/丢包恢复)
|
||
// 当前端重连或显式发送 SyncStateCommand 时,Orchestrator 发送此事件
|
||
WorkflowStateSnapshot {
|
||
timestamp: i64,
|
||
task_graph: WorkflowDag,
|
||
tasks_status: HashMap<String, TaskStatus>, // 当前所有任务的最新状态
|
||
tasks_output: HashMap<String, Option<String>>, // (可选) 已完成任务的关键输出摘要 (commit hash)
|
||
tasks_metadata: HashMap<String, TaskMetadata>, // (New) 任务的关键元数据
|
||
|
||
/// New: Detailed state for each task including logs and content buffer
|
||
#[serde(default)]
|
||
task_states: HashMap<String, TaskStateSnapshot>,
|
||
|
||
logs: Vec<String>, // (New) 当前Session的历史日志回放 (Global)
|
||
}
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct WorkflowDag {
|
||
pub nodes: Vec<TaskNode>,
|
||
pub edges: Vec<TaskDependency> // from -> to
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct TaskDependency {
|
||
pub from: String,
|
||
pub to: String,
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct TaskNode {
|
||
pub id: String,
|
||
pub name: String,
|
||
pub display_name: Option<String>,
|
||
pub r#type: TaskType,
|
||
pub initial_status: TaskStatus
|
||
}
|
||
|
||
#[api_dto]
|
||
#[derive(Copy, PartialEq)]
|
||
pub enum TaskType {
|
||
DataFetch, // 创造原始上下文
|
||
DataProcessing, // 消耗并转换上下文 (New)
|
||
Analysis // 读取上下文生成新内容
|
||
}
|
||
|
||
#[api_dto]
|
||
#[derive(Copy, PartialEq)]
|
||
pub enum TaskStatus {
|
||
Pending, // 等待依赖
|
||
Scheduled, // 依赖满足,已下发给 Worker
|
||
Running, // Worker 正在执行
|
||
Completed, // 执行成功
|
||
Failed, // 执行失败
|
||
Skipped // 因上游失败或策略原因被跳过
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct CompanyProfilePersistedEvent {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct FinancialsPersistedEvent {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub years_updated: Vec<u16>,
|
||
pub template_id: Option<String>, // Pass-through for analysis trigger
|
||
// Identity fix: Mandatory provider ID
|
||
#[serde(default)]
|
||
pub provider_id: Option<String>,
|
||
// Output pass-through: Optional data preview/summary
|
||
#[serde(default)]
|
||
pub data_summary: Option<String>,
|
||
}
|
||
|
||
impl SubjectMessage for FinancialsPersistedEvent {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::DataFinancialsPersisted
|
||
}
|
||
}
|
||
|
||
#[api_dto]
|
||
pub struct DataFetchFailedEvent {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub error: String,
|
||
// Identity fix: Mandatory provider ID
|
||
#[serde(default)]
|
||
pub provider_id: Option<String>,
|
||
}
|
||
|
||
impl SubjectMessage for DataFetchFailedEvent {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::DataFetchFailed
|
||
}
|
||
}
|
||
|
||
// Topic: events.analysis.report_generated
|
||
/// Event emitted when a report generation task (or sub-module) is completed.
|
||
/// Consumed by: `workflow-orchestrator`
|
||
#[api_dto]
|
||
pub struct ReportGeneratedEvent {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub module_id: String, // Which part of the analysis finished
|
||
pub content_snapshot: Option<String>, // Optional short preview
|
||
pub model_id: Option<String>,
|
||
}
|
||
|
||
impl SubjectMessage for ReportGeneratedEvent {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::AnalysisReportGenerated
|
||
}
|
||
}
|
||
|
||
// Topic: events.analysis.report_failed
|
||
#[api_dto]
|
||
pub struct ReportFailedEvent {
|
||
pub request_id: Uuid,
|
||
pub symbol: CanonicalSymbol,
|
||
pub module_id: String,
|
||
pub error: String,
|
||
}
|
||
|
||
impl SubjectMessage for ReportFailedEvent {
|
||
fn subject(&self) -> NatsSubject {
|
||
NatsSubject::AnalysisReportFailed
|
||
}
|
||
}
|