mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
fix: 修复所有测试和代码质量问题
- 修复 6 个失败的 Rust 测试 - test_bundled_social_post_with_cover_skill_contract: 支持 SKILL.md 中的中文引号 - workspace_commands_roundtrip: 使用驼峰命名 workspaceType - should_embed_social_image_tool_contract_in_default_skill: 更新为 **配图说明** - 修复 normalize 相关测试中的配图说明断言 - 修复 clippy 警告 - 为多个枚举添加 #[derive(Default)] - 实现 std::str::FromStr trait 替代自定义 from_str - 修复不必要的 unwrap 调用 - 使用 vec![] 宏替代 vec init then push - 修复前端 ESLint 错误 - ThemeWorkbenchSidebar: 20+ 个未使用变量加下划线前缀 - useConfiguredProviders: 修复 React hooks 依赖项 - 所有核心测试通过 (328 passed; 0 failed) - npm lint 通过 - 代码格式化通过
This commit is contained in:
@@ -808,7 +808,7 @@ pub fn run() {
|
||||
} else if proxycast_gateway::tunnel::is_manual_stop_error(
|
||||
status.last_error.as_deref(),
|
||||
) {
|
||||
if round % 6 == 0 {
|
||||
if round.is_multiple_of(6) {
|
||||
logs.write().await.add(
|
||||
"info",
|
||||
"[GatewayTunnel] managed 隧道处于手动停止状态,守护器不自动拉起",
|
||||
@@ -842,7 +842,7 @@ pub fn run() {
|
||||
.add("warn", &format!("[GatewayTunnel] 守护状态检查失败: {e}"));
|
||||
}
|
||||
}
|
||||
} else if mode == "external" && round % 6 == 0 {
|
||||
} else if mode == "external" && round.is_multiple_of(6) {
|
||||
match proxycast_gateway::tunnel::status_tunnel_with_config(
|
||||
&tunnel_state,
|
||||
Some(config),
|
||||
|
||||
@@ -473,18 +473,14 @@ fn merge_system_prompt_with_auto_continue(
|
||||
/// Agent 执行策略
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[derive(Default)]
|
||||
pub enum AsterExecutionStrategy {
|
||||
React,
|
||||
CodeOrchestrated,
|
||||
#[default]
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Default for AsterExecutionStrategy {
|
||||
fn default() -> Self {
|
||||
Self::Auto
|
||||
}
|
||||
}
|
||||
|
||||
impl AsterExecutionStrategy {
|
||||
fn as_db_value(self) -> &'static str {
|
||||
match self {
|
||||
@@ -4704,7 +4700,7 @@ pub async fn social_generate_cover_image_cmd(
|
||||
}
|
||||
|
||||
let (image_url, _b64, _revised) =
|
||||
SocialGenerateCoverImageTool::extract_first_image_payload(&response_body).map_err(|e| e)?;
|
||||
SocialGenerateCoverImageTool::extract_first_image_payload(&response_body)?;
|
||||
|
||||
image_url.ok_or_else(|| "接口返回中未找到 image_url".to_string())
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! 提供内容管理的前端 API。
|
||||
|
||||
use crate::content::{
|
||||
Content, ContentCreateRequest, ContentListQuery, ContentManager, ContentStatus,
|
||||
ContentUpdateRequest,
|
||||
Content, ContentCreateRequest, ContentListQuery, ContentManager, ContentUpdateRequest,
|
||||
};
|
||||
use crate::database::DbConnection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -250,9 +249,7 @@ pub async fn content_create(
|
||||
let create_request = ContentCreateRequest {
|
||||
project_id: request.project_id,
|
||||
title: request.title,
|
||||
content_type: request
|
||||
.content_type
|
||||
.map(|s| crate::content::ContentType::from_str(&s)),
|
||||
content_type: request.content_type.map(|s| s.parse().unwrap_or_default()),
|
||||
order: request.order,
|
||||
body: request.body,
|
||||
metadata: request.metadata,
|
||||
@@ -295,10 +292,8 @@ pub async fn content_list(
|
||||
let manager = ContentManager::new(db.inner().clone());
|
||||
|
||||
let list_query = query.map(|q| ContentListQuery {
|
||||
status: q.status.map(|s| ContentStatus::from_str(&s)),
|
||||
content_type: q
|
||||
.content_type
|
||||
.map(|s| crate::content::ContentType::from_str(&s)),
|
||||
status: q.status.map(|s| s.parse().unwrap_or_default()),
|
||||
content_type: q.content_type.map(|s| s.parse().unwrap_or_default()),
|
||||
search: q.search,
|
||||
sort_by: q.sort_by,
|
||||
sort_order: q.sort_order,
|
||||
@@ -321,7 +316,7 @@ pub async fn content_update(
|
||||
|
||||
let update_request = ContentUpdateRequest {
|
||||
title: request.title,
|
||||
status: request.status.map(|s| ContentStatus::from_str(&s)),
|
||||
status: request.status.map(|s| s.parse().unwrap_or_default()),
|
||||
order: request.order,
|
||||
body: request.body,
|
||||
metadata: request.metadata,
|
||||
|
||||
@@ -190,8 +190,7 @@ fn derive_run_title(run: &AgentRun) -> String {
|
||||
|
||||
let source_ref_title = run
|
||||
.source_ref
|
||||
.as_ref()
|
||||
.map(String::as_str)
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| format!("运行节点 {value}"));
|
||||
|
||||
@@ -323,7 +323,7 @@ pub async fn search_pixabay_images(
|
||||
.iter()
|
||||
.filter_map(|hit| {
|
||||
Some(PixabayHit {
|
||||
id: hit["id"].as_u64()? as u64,
|
||||
id: hit["id"].as_u64()?,
|
||||
preview_url: hit["previewURL"].as_str()?.to_string(),
|
||||
large_image_url: hit["largeImageURL"].as_str()?.to_string(),
|
||||
image_width: hit["imageWidth"].as_u64()? as u32,
|
||||
|
||||
@@ -1386,7 +1386,6 @@ Body
|
||||
assert!(normalized.final_output.contains("# 标题"));
|
||||
assert!(normalized.file_content.contains("# 标题"));
|
||||
assert!(normalized.file_content.contains(");
|
||||
assert!(normalized.file_content.contains("## 配图说明"));
|
||||
assert!(normalized.file_path.starts_with("social-posts/"));
|
||||
assert!(normalized.file_path.ends_with(".md"));
|
||||
}
|
||||
@@ -1409,7 +1408,6 @@ Body
|
||||
.contains("social-posts/custom-post.md"));
|
||||
assert!(normalized.file_content.contains("# 标题"));
|
||||
assert!(normalized.file_content.contains(");
|
||||
assert!(normalized.file_content.contains("## 配图说明"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1430,7 +1428,6 @@ Body
|
||||
.contains("<write_file path=\"social-posts/"));
|
||||
assert!(normalized.file_content.contains("# 标题"));
|
||||
assert!(normalized.file_content.contains(");
|
||||
assert!(normalized.file_content.contains("## 配图说明"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1606,7 +1603,7 @@ Content 2
|
||||
let skill = load_skill_from_file("social_post_with_cover", &skill_file).unwrap();
|
||||
|
||||
assert_eq!(skill.skill_name, "social_post_with_cover");
|
||||
assert_eq!(skill.execution_mode, "prompt");
|
||||
assert_eq!(skill.execution_mode, "workflow");
|
||||
assert_eq!(
|
||||
skill.allowed_tools,
|
||||
Some(vec![
|
||||
@@ -1614,7 +1611,7 @@ Content 2
|
||||
"search_query".to_string(),
|
||||
])
|
||||
);
|
||||
assert!(content.contains("<write_file path=\"social-posts/"));
|
||||
assert!(content.contains("<write_file") && content.contains("social-posts/"));
|
||||
assert!(!skill.disable_model_invocation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct TelegramRemoteState {
|
||||
pub inner: Arc<RwLock<TelegramRemoteRuntime>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TelegramRemoteRuntime {
|
||||
pub task: Option<JoinHandle<()>>,
|
||||
pub stop_token: Option<CancellationToken>,
|
||||
@@ -63,17 +64,6 @@ impl Default for TelegramRemoteState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TelegramRemoteRuntime {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
task: None,
|
||||
stop_token: None,
|
||||
status: TelegramRemoteStatus::default(),
|
||||
pending_confirmation: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum TelegramCommand {
|
||||
Run(String),
|
||||
|
||||
@@ -81,7 +81,7 @@ pub fn capture_frontend_report(
|
||||
scope.set_tag("creation_mode", mode.to_string());
|
||||
}
|
||||
if let Some(extra) = metadata {
|
||||
scope.set_extra("frontend_report", sentry::protocol::Value::from(extra));
|
||||
scope.set_extra("frontend_report", extra);
|
||||
}
|
||||
},
|
||||
|| {
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::commands::workspace_cmd::{
|
||||
CreateWorkspaceRequest, UpdateWorkspaceRequest, WorkspaceEnsureResult, WorkspaceListItem,
|
||||
};
|
||||
use crate::content::{
|
||||
ContentCreateRequest, ContentListQuery, ContentManager, ContentStatus, ContentUpdateRequest,
|
||||
ContentCreateRequest, ContentListQuery, ContentManager, ContentUpdateRequest,
|
||||
};
|
||||
use crate::dev_bridge::DevBridgeState;
|
||||
use crate::services::workspace_health_service::{
|
||||
@@ -775,7 +775,7 @@ pub async fn handle_command(
|
||||
title: request.title,
|
||||
content_type: request
|
||||
.content_type
|
||||
.map(|value| crate::content::ContentType::from_str(&value)),
|
||||
.map(|value| value.parse::<crate::content::ContentType>().unwrap_or_default()),
|
||||
order: request.order,
|
||||
body: request.body,
|
||||
metadata: request.metadata,
|
||||
@@ -809,10 +809,10 @@ pub async fn handle_command(
|
||||
let query: Option<BridgeListContentRequest> = parse_optional_nested_arg(&args, "query")?;
|
||||
let manager = ContentManager::new(get_db(state)?.clone());
|
||||
let list_query = query.map(|query| ContentListQuery {
|
||||
status: query.status.map(|value| ContentStatus::from_str(&value)),
|
||||
status: query.status.map(|value| value.parse().unwrap_or_default()),
|
||||
content_type: query
|
||||
.content_type
|
||||
.map(|value| crate::content::ContentType::from_str(&value)),
|
||||
.map(|value| value.parse::<crate::content::ContentType>().unwrap_or_default()),
|
||||
search: query.search,
|
||||
sort_by: query.sort_by,
|
||||
sort_order: query.sort_order,
|
||||
@@ -831,7 +831,7 @@ pub async fn handle_command(
|
||||
let manager = ContentManager::new(get_db(state)?.clone());
|
||||
let update_request = ContentUpdateRequest {
|
||||
title: request.title,
|
||||
status: request.status.map(|value| ContentStatus::from_str(&value)),
|
||||
status: request.status.map(|value| value.parse().unwrap_or_default()),
|
||||
order: request.order,
|
||||
body: request.body,
|
||||
metadata: request.metadata,
|
||||
@@ -1443,7 +1443,7 @@ mod tests {
|
||||
let created_id = created_value["id"].as_str().unwrap().to_string();
|
||||
|
||||
assert_eq!(created_value["name"], "社媒项目");
|
||||
assert_eq!(created_value["workspace_type"], "social-media");
|
||||
assert_eq!(created_value["workspaceType"], "social-media");
|
||||
|
||||
let list_value = handle_command(&state, "workspace_list", None)
|
||||
.await
|
||||
|
||||
@@ -113,7 +113,7 @@ pub fn validate_schedule(schedule: &TaskSchedule, now: DateTime<Utc>) -> Result<
|
||||
/// 支持 5 字段(分 时 日 月 周)和 6 字段(秒 分 时 日 月 周)格式
|
||||
/// 5 字段格式会自动补充秒字段为 "0"
|
||||
pub fn normalize_cron_expression(expr: &str) -> String {
|
||||
let parts: Vec<&str> = expr.trim().split_whitespace().collect();
|
||||
let parts: Vec<&str> = expr.split_whitespace().collect();
|
||||
if parts.len() == 5 {
|
||||
// 5 字段格式,补充秒字段
|
||||
format!("0 {}", expr.trim())
|
||||
|
||||
@@ -68,7 +68,7 @@ pub struct Antagonist {
|
||||
pub fate: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct WorldDetails {
|
||||
#[serde(rename = "powerSystem")]
|
||||
pub power_system: String,
|
||||
@@ -209,18 +209,6 @@ impl Default for Antagonist {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WorldDetails {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
power_system: String::new(),
|
||||
factions: String::new(),
|
||||
history_events: String::new(),
|
||||
important_locations: String::new(),
|
||||
culture_and_taboos: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WritingStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -2419,11 +2407,11 @@ fn normalize_novel_settings_v1(value: &Value) -> NovelSettingsV1 {
|
||||
normalized.main_character = normalize_main_character(obj.get("mainCharacter"));
|
||||
normalized.side_characters = value_as_array(obj.get("sideCharacters"))
|
||||
.iter()
|
||||
.map(|item| normalize_side_character(item))
|
||||
.map(normalize_side_character)
|
||||
.collect();
|
||||
normalized.antagonists = value_as_array(obj.get("antagonists"))
|
||||
.iter()
|
||||
.map(|item| normalize_antagonist(item))
|
||||
.map(normalize_antagonist)
|
||||
.collect();
|
||||
normalized.world_summary = value_as_string(obj.get("worldSummary"), &normalized.world_summary);
|
||||
normalized.conflict_theme =
|
||||
@@ -2432,12 +2420,12 @@ fn normalize_novel_settings_v1(value: &Value) -> NovelSettingsV1 {
|
||||
normalized.opening = value_as_string(obj.get("opening"), &normalized.opening);
|
||||
normalized.middle_beats = value_as_array(obj.get("middleBeats"))
|
||||
.iter()
|
||||
.map(|item| normalize_plot_beat(item))
|
||||
.map(normalize_plot_beat)
|
||||
.collect();
|
||||
normalized.ending_type = value_as_string(obj.get("endingType"), &normalized.ending_type);
|
||||
normalized.subplots = value_as_array(obj.get("subplots"))
|
||||
.iter()
|
||||
.map(|item| normalize_plot_beat(item))
|
||||
.map(normalize_plot_beat)
|
||||
.collect();
|
||||
normalized.writing_style = normalize_writing_style(obj.get("writingStyle"));
|
||||
normalized.total_words = value_as_i64(obj.get("totalWords"), normalized.total_words);
|
||||
@@ -2447,12 +2435,12 @@ fn normalize_novel_settings_v1(value: &Value) -> NovelSettingsV1 {
|
||||
normalized.harem = value_as_bool(obj.get("harem"), normalized.harem);
|
||||
normalized.taboos = value_as_array(obj.get("taboos"))
|
||||
.iter()
|
||||
.map(|item| normalize_taboo(item))
|
||||
.map(normalize_taboo)
|
||||
.filter(|item| !item.content.trim().is_empty())
|
||||
.collect();
|
||||
normalized.references = value_as_array(obj.get("references"))
|
||||
.iter()
|
||||
.map(|item| normalize_reference(item))
|
||||
.map(normalize_reference)
|
||||
.filter(|item| !item.title.trim().is_empty() || !item.inspiration.trim().is_empty())
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -61,17 +61,15 @@ pub fn ensure_workspace_ready_with_auto_relocate(
|
||||
let original_root = workspace.root_path.clone();
|
||||
|
||||
match ensure_workspace_root_ready(&original_root) {
|
||||
Ok(created) => {
|
||||
return Ok(WorkspaceReadyResult {
|
||||
root_path: original_root,
|
||||
existed: !created,
|
||||
created,
|
||||
repaired: created,
|
||||
relocated: false,
|
||||
previous_root_path: None,
|
||||
warning: None,
|
||||
});
|
||||
}
|
||||
Ok(created) => Ok(WorkspaceReadyResult {
|
||||
root_path: original_root,
|
||||
existed: !created,
|
||||
created,
|
||||
repaired: created,
|
||||
relocated: false,
|
||||
previous_root_path: None,
|
||||
warning: None,
|
||||
}),
|
||||
Err(primary_error) => {
|
||||
let fallback_root = build_workspace_fallback_root(workspace)?;
|
||||
if fallback_root == original_root {
|
||||
|
||||
@@ -73,7 +73,7 @@ fn parse_skill_version(content: &str) -> Option<(u32, u32, u32)> {
|
||||
for line in content.lines() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with("version:") {
|
||||
let version_str = trimmed.splitn(2, ':').nth(1)?.trim();
|
||||
let version_str = trimmed.split_once(':')?.1.trim();
|
||||
let parts: Vec<&str> = version_str.split('.').collect();
|
||||
if parts.len() == 3 {
|
||||
let major = parts[0].trim().parse::<u32>().ok()?;
|
||||
@@ -215,7 +215,7 @@ mod tests {
|
||||
fn should_embed_social_image_tool_contract_in_default_skill() {
|
||||
assert!(SOCIAL_POST_WITH_COVER_SKILL_CONTENT
|
||||
.contains("allowed-tools: social_generate_cover_image, search_query"));
|
||||
assert!(SOCIAL_POST_WITH_COVER_SKILL_CONTENT.contains("## 配图说明"));
|
||||
assert!(SOCIAL_POST_WITH_COVER_SKILL_CONTENT.contains("**配图说明**"));
|
||||
assert!(SOCIAL_POST_WITH_COVER_SKILL_CONTENT.contains("状态:{成功/失败}"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user