CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。
85 lines
3.1 KiB
Rust
85 lines
3.1 KiB
Rust
//! `get_config_schema`:把 M12 的配置 schema(**只含 AI 可写键**)投给模型。
|
||
//!
|
||
//! 为什么单独一个工具而不是塞进 `get_config`:模型要能「先看有哪些键、类型是什么、
|
||
//! 哪些是敏感键」,再决定要不要写——这是一次性的静态信息,和读当前值不是同一件事。
|
||
//! 拆开还能让 token 预算更可控(schema 比概要大一个量级)。
|
||
//!
|
||
//! 凭据边界:schema 由 M12 的 `SettingSchema::to_json_schema()` 生成,只遍历
|
||
//! `ai.writable && !deprecated` 的键,`ai.api_key`(`locked`)天然不在其中。
|
||
|
||
use std::sync::Arc;
|
||
|
||
use serde_json::{json, Value};
|
||
|
||
use crate::port::SettingPort;
|
||
use crate::registry::schema::object;
|
||
use crate::registry::{
|
||
RegistryError, ToolCtx, ToolError, ToolFn, ToolOutput, ToolRegistry, ToolSpec,
|
||
};
|
||
|
||
/// 工具名。
|
||
pub const GET_CONFIG_SCHEMA: &str = "get_config_schema";
|
||
|
||
/// 注册 `get_config_schema`。
|
||
///
|
||
/// # Errors
|
||
/// 工具名重复/schema 非法时返回 [`RegistryError`]。
|
||
pub fn register(
|
||
registry: &mut ToolRegistry,
|
||
port: Arc<dyn SettingPort>,
|
||
) -> Result<(), RegistryError> {
|
||
let handler: ToolFn = Arc::new(move |_ctx: &ToolCtx, _args: &Value| {
|
||
let schema = port.schema();
|
||
if schema.get("properties").is_none() {
|
||
return Err(ToolError::Port(
|
||
"配置 schema 不可用:装配层没有提供 SettingPort::schema()".to_string(),
|
||
));
|
||
}
|
||
let writable = schema
|
||
.get("properties")
|
||
.and_then(Value::as_object)
|
||
.map_or(0, serde_json::Map::len);
|
||
Ok(ToolOutput::ok(
|
||
json!({
|
||
"scope": "ai_writable_schema",
|
||
"writable_key_count": writable,
|
||
"schema": schema,
|
||
}),
|
||
format!("读取配置 schema({writable} 个 AI 可写键)"),
|
||
))
|
||
});
|
||
|
||
registry.register(ToolSpec::read_only(
|
||
GET_CONFIG_SCHEMA,
|
||
"读取启动器配置的 JSON Schema(只含 AI 可写键)。每个键带类型、取值范围、说明与 `x-ai-sensitivity` 敏感等级;凭据类键不会出现。写配置前先看它。",
|
||
object(&[], &[]),
|
||
handler,
|
||
))
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::testing::MockSettings;
|
||
|
||
fn ctx() -> ToolCtx {
|
||
ToolCtx {
|
||
agent_session_id: "s1".to_string(),
|
||
confirmed: false,
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn returns_schema_without_credentials() {
|
||
let port: Arc<dyn SettingPort> = Arc::new(MockSettings::new());
|
||
let mut registry = ToolRegistry::new();
|
||
register(&mut registry, port).expect("注册应成功");
|
||
let spec = registry.get(GET_CONFIG_SCHEMA).expect("工具存在").clone();
|
||
let out = (spec.handler)(&ctx(), &json!({})).expect("应返回 schema");
|
||
let text = out.content.to_string();
|
||
assert!(out.content["writable_key_count"].as_u64().unwrap_or(0) > 0);
|
||
assert!(text.contains("x-ai-sensitivity"));
|
||
assert!(!text.contains("ai.api_key"), "凭据键不得进 schema:{text}");
|
||
}
|
||
}
|