d4075c3423
新增纯 Rust agent-runtime-core,提供运行时契约、能力注册、Agent 目录、Profile、完成策略与零重放恢复 抽取中立 LLM Provider 协议与可扩展 Registry,并适配 OpenAI Responses、OpenAI Chat 和 Anthropic 将 AGC interaction、Provider 控制、Runner 生命周期、steering 与 tool-plan handoff 接入统一运行时边界 补充非游戏消费者、Provider 网络闭环、Runtime 恢复及 GUI Runner owner 测试 同步 Cargo/npm 门禁、Runtime 技术方案和项目共享记忆
122 lines
3.4 KiB
Rust
122 lines
3.4 KiB
Rust
use std::fmt;
|
|
|
|
use serde_json::Value;
|
|
|
|
const IDENTIFIER_MAX_CHARS: usize = 128;
|
|
const DESCRIPTION_MAX_CHARS: usize = 4_000;
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub enum ContractErrorKind {
|
|
InvalidDefinition,
|
|
DuplicateId,
|
|
DuplicateReference,
|
|
UnknownCapability,
|
|
UnknownCompletionPolicy,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct ContractError {
|
|
kind: ContractErrorKind,
|
|
detail: String,
|
|
}
|
|
|
|
impl ContractError {
|
|
pub fn new(kind: ContractErrorKind, detail: impl Into<String>) -> Self {
|
|
Self {
|
|
kind,
|
|
detail: detail.into(),
|
|
}
|
|
}
|
|
|
|
pub fn kind(&self) -> ContractErrorKind {
|
|
self.kind
|
|
}
|
|
|
|
pub fn detail(&self) -> &str {
|
|
&self.detail
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ContractError {
|
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
formatter.write_str(&self.detail)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for ContractError {}
|
|
|
|
pub(crate) fn validate_identifier(value: &str, field: &str) -> Result<(), ContractError> {
|
|
if value != value.trim() {
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("{field} 不得包含首尾空白"),
|
|
));
|
|
}
|
|
let mut chars = value.chars();
|
|
let first = chars.next().ok_or_else(|| {
|
|
ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("{field} 不能为空"),
|
|
)
|
|
})?;
|
|
if value.chars().count() > IDENTIFIER_MAX_CHARS
|
|
|| !first.is_ascii_alphanumeric()
|
|
|| !chars.all(|character| {
|
|
character.is_ascii_alphanumeric() || matches!(character, '.' | '_' | '-' | ':')
|
|
})
|
|
{
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("{field} 不是合法稳定标识:{value}"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn validate_function_name(value: &str) -> Result<(), ContractError> {
|
|
if value != value.trim() {
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
"functionName 不得包含首尾空白",
|
|
));
|
|
}
|
|
let mut chars = value.chars();
|
|
let first = chars.next().ok_or_else(|| {
|
|
ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
"functionName 不能为空",
|
|
)
|
|
})?;
|
|
if value.chars().count() > IDENTIFIER_MAX_CHARS
|
|
|| !(first.is_ascii_alphabetic() || first == '_')
|
|
|| !chars.all(|character| character.is_ascii_alphanumeric() || character == '_')
|
|
{
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("functionName 不是合法函数标识:{value}"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn validate_description(value: &str, field: &str) -> Result<(), ContractError> {
|
|
let chars = value.trim().chars().count();
|
|
if chars == 0 || chars > DESCRIPTION_MAX_CHARS {
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("{field} 必须为 1..={DESCRIPTION_MAX_CHARS} 个字符"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn validate_metadata(metadata: &Value, field: &str) -> Result<(), ContractError> {
|
|
if !metadata.is_object() {
|
|
return Err(ContractError::new(
|
|
ContractErrorKind::InvalidDefinition,
|
|
format!("{field} 必须是 JSON object"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|