mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 15:40:18 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab806efec4 | ||
|
|
83675c91da |
Generated
+1
-1
@@ -269,7 +269,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whatsapp-channel"
|
name = "whatsapp-channel"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ impl WasmToolLoader {
|
|||||||
tool = name,
|
tool = name,
|
||||||
path = %cap_path.display(),
|
path = %cap_path.display(),
|
||||||
"Capabilities file missing \"description\" field; \
|
"Capabilities file missing \"description\" field; \
|
||||||
tool will use generic fallback description"
|
using WASM-exported description when available"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if params.is_none() && cap_file.parameters.is_none() {
|
if params.is_none() && cap_file.parameters.is_none() {
|
||||||
@@ -171,7 +171,7 @@ impl WasmToolLoader {
|
|||||||
tool = name,
|
tool = name,
|
||||||
path = %cap_path.display(),
|
path = %cap_path.display(),
|
||||||
"Capabilities file missing \"parameters\" field; \
|
"Capabilities file missing \"parameters\" field; \
|
||||||
tool will accept any JSON object (permissive fallback)"
|
using exported WASM schema when available"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(caps, oauth, desc, params)
|
(caps, oauth, desc, params)
|
||||||
@@ -186,7 +186,7 @@ impl WasmToolLoader {
|
|||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
tool = name,
|
tool = name,
|
||||||
"No capabilities file for WASM tool; \
|
"No capabilities file for WASM tool; \
|
||||||
tool will use generic fallback description and accept any JSON object"
|
using default permissions and WASM-exported metadata when available"
|
||||||
);
|
);
|
||||||
(Capabilities::default(), None, None, None)
|
(Capabilities::default(), None, None, None)
|
||||||
};
|
};
|
||||||
|
|||||||
+68
-19
@@ -493,6 +493,10 @@ struct WasmToolSchemas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WasmToolSchemas {
|
impl WasmToolSchemas {
|
||||||
|
/// Keep advertised schemas reasonably small because they are serialized
|
||||||
|
/// into the main tool list shown to the model.
|
||||||
|
const MAX_ADVERTISED_SCHEMA_BYTES: usize = 8 * 1024;
|
||||||
|
|
||||||
fn permissive_schema() -> serde_json::Value {
|
fn permissive_schema() -> serde_json::Value {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -521,9 +525,20 @@ impl WasmToolSchemas {
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn should_advertise_discovery(schema: &serde_json::Value) -> bool {
|
||||||
|
Self::typed_property_count(schema) > 0
|
||||||
|
&& schema.to_string().len() <= Self::MAX_ADVERTISED_SCHEMA_BYTES
|
||||||
|
}
|
||||||
|
|
||||||
fn new(discovery: serde_json::Value) -> Self {
|
fn new(discovery: serde_json::Value) -> Self {
|
||||||
|
let advertised = if Self::should_advertise_discovery(&discovery) {
|
||||||
|
discovery.clone()
|
||||||
|
} else {
|
||||||
|
Self::permissive_schema()
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
advertised: Self::permissive_schema(),
|
advertised,
|
||||||
discovery,
|
discovery,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1490,7 +1505,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_advertised_schema_stays_permissive_until_sidecar_override() {
|
async fn test_small_exported_schema_is_advertised_directly() {
|
||||||
let discovery_schema = serde_json::json!({
|
let discovery_schema = serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -1510,30 +1525,19 @@ mod tests {
|
|||||||
wrapper.schemas = super::WasmToolSchemas::new(discovery_schema.clone());
|
wrapper.schemas = super::WasmToolSchemas::new(discovery_schema.clone());
|
||||||
wrapper.description = "Search documents".to_string();
|
wrapper.description = "Search documents".to_string();
|
||||||
|
|
||||||
// Advertised schema stays permissive; discovery holds the typed schema
|
// Small typed exported schemas should be advertised directly so the
|
||||||
assert_eq!(
|
// model sees the actual required parameters.
|
||||||
wrapper.parameters_schema(),
|
assert_eq!(wrapper.parameters_schema(), discovery_schema);
|
||||||
serde_json::json!({
|
|
||||||
"type": "object",
|
|
||||||
"properties": {},
|
|
||||||
"additionalProperties": true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
assert_eq!(wrapper.discovery_schema(), discovery_schema);
|
assert_eq!(wrapper.discovery_schema(), discovery_schema);
|
||||||
|
|
||||||
// Raw description is clean — no tool_info hint baked in
|
// Raw description is clean — no tool_info hint baked in
|
||||||
assert!(!wrapper.description().contains("tool_info"));
|
assert!(!wrapper.description().contains("tool_info"));
|
||||||
|
|
||||||
// But schema() composes the hint at display time when advertised is permissive
|
// When advertised is typed, schema() should not add a tool_info hint.
|
||||||
let schema = wrapper.schema();
|
let schema = wrapper.schema();
|
||||||
assert!(
|
assert!(
|
||||||
schema.description.contains("tool_info"),
|
!schema.description.contains("tool_info"),
|
||||||
"schema().description should contain tool_info hint: {}",
|
"schema().description should not contain tool_info hint when typed: {}",
|
||||||
schema.description
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
schema.description.contains("include_schema: true"),
|
|
||||||
"hint should mention include_schema: true: {}",
|
|
||||||
schema.description
|
schema.description
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1567,6 +1571,51 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_large_exported_schema_stays_permissive_for_advertising() {
|
||||||
|
let mut properties = serde_json::Map::new();
|
||||||
|
for i in 0..200 {
|
||||||
|
properties.insert(
|
||||||
|
format!("field_{i:03}"),
|
||||||
|
serde_json::json!({
|
||||||
|
"type": "string",
|
||||||
|
"description": "x".repeat(64)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let discovery_schema = serde_json::json!({
|
||||||
|
"type": "object",
|
||||||
|
"properties": properties,
|
||||||
|
});
|
||||||
|
|
||||||
|
let runtime = Arc::new(WasmToolRuntime::new(WasmRuntimeConfig::for_testing()).unwrap());
|
||||||
|
let prepared = runtime
|
||||||
|
.prepare("search", b"\0asm\x0d\0\x01\0", None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut wrapper =
|
||||||
|
super::WasmToolWrapper::new(Arc::clone(&runtime), prepared, Capabilities::default());
|
||||||
|
wrapper.schemas = super::WasmToolSchemas::new(discovery_schema.clone());
|
||||||
|
wrapper.description = "Search documents".to_string();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
wrapper.parameters_schema(),
|
||||||
|
serde_json::json!({
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"additionalProperties": true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(wrapper.discovery_schema(), discovery_schema);
|
||||||
|
|
||||||
|
let schema = wrapper.schema();
|
||||||
|
assert!(
|
||||||
|
schema.description.contains("tool_info"),
|
||||||
|
"large schemas should still fall back to the tool_info hint: {}",
|
||||||
|
schema.description
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_capabilities_default() {
|
fn test_capabilities_default() {
|
||||||
let caps = Capabilities::default();
|
let caps = Capabilities::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user