feat(ai): 创建 erp-ai crate 骨架 + 错误类型
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
12
Cargo.toml
12
Cargo.toml
@@ -16,6 +16,7 @@ members = [
|
|||||||
"crates/erp-plugin-freelance",
|
"crates/erp-plugin-freelance",
|
||||||
"crates/erp-plugin-itops",
|
"crates/erp-plugin-itops",
|
||||||
"crates/erp-health",
|
"crates/erp-health",
|
||||||
|
"crates/erp-ai",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
@@ -81,7 +82,7 @@ validator = { version = "0.19", features = ["derive"] }
|
|||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
# HTTP client
|
# HTTP client
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json", "stream"] }
|
||||||
|
|
||||||
# Crypto
|
# Crypto
|
||||||
aes = "0.8"
|
aes = "0.8"
|
||||||
@@ -100,3 +101,12 @@ erp-message = { path = "crates/erp-message" }
|
|||||||
erp-config = { path = "crates/erp-config" }
|
erp-config = { path = "crates/erp-config" }
|
||||||
erp-plugin = { path = "crates/erp-plugin" }
|
erp-plugin = { path = "crates/erp-plugin" }
|
||||||
erp-health = { path = "crates/erp-health" }
|
erp-health = { path = "crates/erp-health" }
|
||||||
|
erp-ai = { path = "crates/erp-ai" }
|
||||||
|
|
||||||
|
# Async streaming
|
||||||
|
futures = "0.3"
|
||||||
|
tokio-stream = "0.1"
|
||||||
|
async-stream = "0.3"
|
||||||
|
|
||||||
|
# Template engine
|
||||||
|
handlebars = "6"
|
||||||
|
|||||||
25
crates/erp-ai/Cargo.toml
Normal file
25
crates/erp-ai/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "erp-ai"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
erp-core.workspace = true
|
||||||
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
tokio-stream.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
async-stream.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
axum.workspace = true
|
||||||
|
sea-orm.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
utoipa.workspace = true
|
||||||
|
async-trait.workspace = true
|
||||||
|
reqwest = { version = "0.12", features = ["stream", "json"] }
|
||||||
|
handlebars = "6"
|
||||||
|
sha2 = "0.10"
|
||||||
|
hex = "0.4"
|
||||||
59
crates/erp-ai/src/error.rs
Normal file
59
crates/erp-ai/src/error.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use erp_core::error::AppError;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum AiError {
|
||||||
|
#[error("验证失败: {0}")]
|
||||||
|
Validation(String),
|
||||||
|
|
||||||
|
#[error("分析未找到: {0}")]
|
||||||
|
AnalysisNotFound(String),
|
||||||
|
|
||||||
|
#[error("Prompt 模板未找到: {0}")]
|
||||||
|
PromptNotFound(String),
|
||||||
|
|
||||||
|
#[error("AI 提供商不可用: {0}")]
|
||||||
|
ProviderUnavailable(String),
|
||||||
|
|
||||||
|
#[error("AI 提供商错误: {0}")]
|
||||||
|
ProviderError(String),
|
||||||
|
|
||||||
|
#[error("数据脱敏失败: {0}")]
|
||||||
|
SanitizationError(String),
|
||||||
|
|
||||||
|
#[error("模板渲染失败: {0}")]
|
||||||
|
TemplateError(String),
|
||||||
|
|
||||||
|
#[error("速率超限")]
|
||||||
|
RateLimitExceeded,
|
||||||
|
|
||||||
|
#[error("版本不匹配")]
|
||||||
|
VersionMismatch,
|
||||||
|
|
||||||
|
#[error("数据库错误: {0}")]
|
||||||
|
DbError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AiError> for AppError {
|
||||||
|
fn from(e: AiError) -> Self {
|
||||||
|
match e {
|
||||||
|
AiError::Validation(msg) => AppError::Validation(msg),
|
||||||
|
AiError::AnalysisNotFound(id) => AppError::NotFound(format!("分析结果: {id}")),
|
||||||
|
AiError::PromptNotFound(name) => AppError::NotFound(format!("Prompt 模板: {name}")),
|
||||||
|
AiError::ProviderUnavailable(p) => {
|
||||||
|
AppError::Internal(format!("AI 提供商 {p} 不可用"))
|
||||||
|
}
|
||||||
|
AiError::RateLimitExceeded => AppError::TooManyRequests,
|
||||||
|
AiError::VersionMismatch => AppError::VersionMismatch,
|
||||||
|
AiError::DbError(msg) => AppError::Internal(msg),
|
||||||
|
other => AppError::Internal(other.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sea_orm::DbErr> for AiError {
|
||||||
|
fn from(e: sea_orm::DbErr) -> Self {
|
||||||
|
AiError::DbError(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AiResult<T> = Result<T, AiError>;
|
||||||
3
crates/erp-ai/src/lib.rs
Normal file
3
crates/erp-ai/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub use error::{AiError, AiResult};
|
||||||
Reference in New Issue
Block a user