sdk, agent: review fixes (T18R)
- The serve loop ends the process after EOF or exit_after_response, also with background tasks or listeners. - One schema generator per direction for the whole document, so types with the same name get distinct names. - The agent gives the complete error (code, message, data) to the model, writes SKIP only after a successful request, caps tool output at 64 KiB, writes each history line in one write, and skips a damaged line. - Endpoints skip blank and non-RPC lines. client_rs does not panic. - #[must_use] on builders and the lock guard; #[schemars] attributes and duplicate hooks are compile errors; raw identifiers; -32000 for local SDK errors; x-sausage-params only for methods with params.
This commit is contained in:
+45
-30
@@ -31,16 +31,7 @@ impl<T: SchemaType> SchemaType for Option<T> {}
|
||||
|
||||
/// Create an OpenRPC document builder for generated interfaces.
|
||||
#[doc(hidden)]
|
||||
pub fn document(title: &str, version: &str) -> Value {
|
||||
let mut doc = sausage_interface::Document::new(title, version).into_value();
|
||||
doc["x-sdk-input"] = json!({});
|
||||
doc["x-sdk-output"] = json!({});
|
||||
doc
|
||||
}
|
||||
|
||||
/// Add a method with independent deserialize and serialize contracts.
|
||||
#[doc(hidden)]
|
||||
pub fn method<P: Params, R: SchemaType>(doc: &mut Value, name: &str, docs: &str) {
|
||||
pub fn document(title: &str, version: &str) -> Discovery {
|
||||
fn generator(serialize: bool) -> schemars::SchemaGenerator {
|
||||
let mut settings = schemars::generate::SchemaSettings::draft07();
|
||||
settings.definitions_path = if serialize {
|
||||
@@ -56,10 +47,44 @@ pub fn method<P: Params, R: SchemaType>(doc: &mut Value, name: &str, docs: &str)
|
||||
}
|
||||
.into_generator()
|
||||
}
|
||||
let mut input = generator(false);
|
||||
let params_ref = serde_json::to_value(input.subschema_for::<P>()).unwrap();
|
||||
let definitions = input.take_definitions(true);
|
||||
let schema = &definitions[P::schema_name().as_ref()];
|
||||
Discovery {
|
||||
value: sausage_interface::Document::new(title, version).into_value(),
|
||||
input: generator(false),
|
||||
output: generator(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared generators for all methods and endpoint types in one document.
|
||||
#[doc(hidden)]
|
||||
pub struct Discovery {
|
||||
/// The OpenRPC document before reference resolution.
|
||||
pub value: Value,
|
||||
input: schemars::SchemaGenerator,
|
||||
output: schemars::SchemaGenerator,
|
||||
}
|
||||
impl Discovery {
|
||||
/// Add an endpoint with the same input and output generators.
|
||||
pub fn endpoint<T: Interface>(&mut self, name: &str) {
|
||||
let methods = std::mem::replace(&mut self.value["methods"], json!([]));
|
||||
T::add_methods(self);
|
||||
let endpoint = std::mem::replace(&mut self.value["methods"], methods);
|
||||
self.value["x-sausage-endpoints"][name] = json!({"methods":endpoint});
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a method with independent deserialize and serialize contracts.
|
||||
#[doc(hidden)]
|
||||
pub fn method<P: Params, R: SchemaType>(doc: &mut Discovery, name: &str, docs: &str) {
|
||||
let params_ref = if P::schema_id() == <Empty as schemars::JsonSchema>::schema_id() {
|
||||
Value::Null
|
||||
} else {
|
||||
serde_json::to_value(doc.input.subschema_for::<P>()).unwrap()
|
||||
};
|
||||
let schema = params_ref["$ref"]
|
||||
.as_str()
|
||||
.and_then(|reference| reference.strip_prefix("#/sdk-input/"))
|
||||
.and_then(|name| doc.input.definitions().get(name))
|
||||
.unwrap_or(&Value::Null);
|
||||
let params:Vec<_>=schema["properties"].as_object().into_iter().flatten().map(|(name,field)| {
|
||||
json!({
|
||||
"name": name,
|
||||
@@ -68,18 +93,7 @@ pub fn method<P: Params, R: SchemaType>(doc: &mut Value, name: &str, docs: &str)
|
||||
"schema": field,
|
||||
})
|
||||
}).collect();
|
||||
if !params.is_empty() {
|
||||
doc["x-sdk-input"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.extend(definitions);
|
||||
}
|
||||
let mut output = generator(true);
|
||||
let result_schema = serde_json::to_value(output.subschema_for::<R>()).unwrap();
|
||||
doc["x-sdk-output"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.extend(output.take_definitions(true));
|
||||
let result_schema = serde_json::to_value(doc.output.subschema_for::<R>()).unwrap();
|
||||
let has_params = !params.is_empty();
|
||||
let mut method = json!({
|
||||
"name":name,
|
||||
@@ -92,14 +106,15 @@ pub fn method<P: Params, R: SchemaType>(doc: &mut Value, name: &str, docs: &str)
|
||||
if has_params {
|
||||
method["x-sausage-params"] = params_ref;
|
||||
}
|
||||
doc["methods"].as_array_mut().unwrap().push(method);
|
||||
doc.value["methods"].as_array_mut().unwrap().push(method);
|
||||
}
|
||||
|
||||
/// Resolve shared schema names after all methods contribute their contracts.
|
||||
#[doc(hidden)]
|
||||
pub fn finish_document(mut doc: Value) -> Value {
|
||||
let input = doc.as_object_mut().unwrap().remove("x-sdk-input").unwrap();
|
||||
let output = doc.as_object_mut().unwrap().remove("x-sdk-output").unwrap();
|
||||
pub fn finish_document(mut discovery: Discovery) -> Value {
|
||||
let input = serde_json::to_value(discovery.input.take_definitions(true)).unwrap();
|
||||
let output = serde_json::to_value(discovery.output.take_definitions(true)).unwrap();
|
||||
let mut doc = discovery.value;
|
||||
fn refs(value: &mut Value, names: &std::collections::HashMap<String, String>) {
|
||||
match value {
|
||||
Value::Object(map) => {
|
||||
|
||||
Reference in New Issue
Block a user