chore: 提交所有工作进度 — SaaS 后端增强、Admin UI、桌面端集成
包含大量 SaaS 平台改进、Admin 管理后台更新、桌面端集成完善、 文档同步、测试文件重构等内容。为 QA 测试准备干净工作树。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! Agent configuration and state types
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use crate::{AgentId, Capability, ModelConfig};
|
||||
|
||||
/// Agent configuration
|
||||
@@ -31,6 +32,13 @@ pub struct AgentConfig {
|
||||
/// Temperature (0.0 - 1.0)
|
||||
#[serde(default)]
|
||||
pub temperature: Option<f32>,
|
||||
/// Workspace directory for file access tools
|
||||
#[serde(default)]
|
||||
pub workspace: Option<PathBuf>,
|
||||
/// Optional compaction threshold override (tokens).
|
||||
/// Overrides the dynamic calculation from context_window * 0.6.
|
||||
#[serde(default)]
|
||||
pub compaction_threshold: Option<u32>,
|
||||
/// Whether the agent is active
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
@@ -52,6 +60,8 @@ impl Default for AgentConfig {
|
||||
tools: Vec::new(),
|
||||
max_tokens: None,
|
||||
temperature: None,
|
||||
workspace: None,
|
||||
compaction_threshold: None,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
196
crates/zclaw-types/src/hand_run.rs
Normal file
196
crates/zclaw-types/src/hand_run.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
//! Hand execution tracking types
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Unique identifier for a hand execution run
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct HandRunId(pub Uuid);
|
||||
|
||||
impl HandRunId {
|
||||
pub fn new() -> Self {
|
||||
Self(Uuid::new_v4())
|
||||
}
|
||||
|
||||
pub fn as_uuid(&self) -> &Uuid {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HandRunId {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HandRunId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for HandRunId {
|
||||
type Err = uuid::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Uuid::parse_str(s).map(HandRunId)
|
||||
}
|
||||
}
|
||||
|
||||
/// Status of a hand execution run
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HandRunStatus {
|
||||
/// Created but not yet started
|
||||
Pending,
|
||||
/// Currently executing
|
||||
Running,
|
||||
/// Completed successfully
|
||||
Completed,
|
||||
/// Failed with an error
|
||||
Failed,
|
||||
/// Cancelled by user
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HandRunStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
HandRunStatus::Pending => write!(f, "pending"),
|
||||
HandRunStatus::Running => write!(f, "running"),
|
||||
HandRunStatus::Completed => write!(f, "completed"),
|
||||
HandRunStatus::Failed => write!(f, "failed"),
|
||||
HandRunStatus::Cancelled => write!(f, "cancelled"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for HandRunStatus {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"pending" => Ok(HandRunStatus::Pending),
|
||||
"running" => Ok(HandRunStatus::Running),
|
||||
"completed" => Ok(HandRunStatus::Completed),
|
||||
"failed" => Ok(HandRunStatus::Failed),
|
||||
"cancelled" => Ok(HandRunStatus::Cancelled),
|
||||
other => Err(format!("Unknown HandRunStatus: {}", other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What triggered the hand execution
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TriggerSource {
|
||||
/// Manual invocation from user
|
||||
Manual,
|
||||
/// Scheduled trigger
|
||||
Scheduled { trigger_id: String },
|
||||
/// Event-based trigger
|
||||
Event { trigger_id: String },
|
||||
/// Approval response
|
||||
Approval { approval_id: String },
|
||||
}
|
||||
|
||||
/// A single hand execution run record
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HandRun {
|
||||
/// Unique run identifier
|
||||
pub id: HandRunId,
|
||||
/// Which hand was executed
|
||||
pub hand_name: String,
|
||||
/// What triggered this run
|
||||
pub trigger_source: TriggerSource,
|
||||
/// Input parameters
|
||||
pub params: serde_json::Value,
|
||||
/// Current status
|
||||
pub status: HandRunStatus,
|
||||
/// Output result (set on completion)
|
||||
pub result: Option<serde_json::Value>,
|
||||
/// Error message (set on failure)
|
||||
pub error: Option<String>,
|
||||
/// Duration in milliseconds (set on completion/failure)
|
||||
pub duration_ms: Option<u64>,
|
||||
/// When the run was created (entered Pending)
|
||||
pub created_at: String,
|
||||
/// When execution started (Pending → Running)
|
||||
pub started_at: Option<String>,
|
||||
/// When execution finished (Running → Completed/Failed/Cancelled)
|
||||
pub completed_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Filter parameters for listing hand runs
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct HandRunFilter {
|
||||
/// Filter by hand name
|
||||
pub hand_name: Option<String>,
|
||||
/// Filter by status
|
||||
pub status: Option<HandRunStatus>,
|
||||
/// Maximum number of results
|
||||
pub limit: Option<u32>,
|
||||
/// Number of results to skip
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hand_run_id_unique() {
|
||||
let id1 = HandRunId::new();
|
||||
let id2 = HandRunId::new();
|
||||
assert_ne!(id1, id2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hand_run_id_display() {
|
||||
let id = HandRunId::new();
|
||||
assert_eq!(id.to_string().len(), 36);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hand_run_id_from_str() {
|
||||
let id = HandRunId::new();
|
||||
let parsed: HandRunId = id.to_string().parse().unwrap();
|
||||
assert_eq!(id, parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hand_run_status_roundtrip() {
|
||||
for status in [
|
||||
HandRunStatus::Pending,
|
||||
HandRunStatus::Running,
|
||||
HandRunStatus::Completed,
|
||||
HandRunStatus::Failed,
|
||||
HandRunStatus::Cancelled,
|
||||
] {
|
||||
let s = status.to_string();
|
||||
let parsed: HandRunStatus = s.parse().unwrap();
|
||||
assert_eq!(status, parsed);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hand_run_serialization() {
|
||||
let run = HandRun {
|
||||
id: HandRunId::new(),
|
||||
hand_name: "browser".to_string(),
|
||||
trigger_source: TriggerSource::Manual,
|
||||
params: serde_json::json!({"url": "https://example.com"}),
|
||||
status: HandRunStatus::Completed,
|
||||
result: Some(serde_json::json!({"title": "Example"})),
|
||||
error: None,
|
||||
duration_ms: Some(1500),
|
||||
created_at: "2026-01-01T00:00:00Z".to_string(),
|
||||
started_at: Some("2026-01-01T00:00:00Z".to_string()),
|
||||
completed_at: Some("2026-01-01T00:00:01Z".to_string()),
|
||||
};
|
||||
let json = serde_json::to_string(&run).unwrap();
|
||||
let deserialized: HandRun = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(run.id, deserialized.id);
|
||||
assert_eq!(run.hand_name, deserialized.hand_name);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub mod error;
|
||||
pub mod event;
|
||||
pub mod tool;
|
||||
pub mod config;
|
||||
pub mod hand_run;
|
||||
|
||||
pub use id::*;
|
||||
pub use message::*;
|
||||
@@ -19,6 +20,7 @@ pub use error::*;
|
||||
pub use event::*;
|
||||
pub use tool::*;
|
||||
pub use config::*;
|
||||
pub use hand_run::*;
|
||||
|
||||
// Re-export commonly used external types
|
||||
pub use serde_json::Value as JsonValue;
|
||||
|
||||
Reference in New Issue
Block a user