not optional

This commit is contained in:
Ahmed Ibrahim
2025-08-23 23:36:10 -07:00
parent a79c71ec00
commit a8d258f7f0
4 changed files with 25 additions and 41 deletions

View File

@@ -2153,11 +2153,10 @@ async fn handle_function_call(
match result {
Ok(message) => {
// If this subagent declares an output schema, validate against it and
// return the JSON body unmodified. Otherwise fallback to envelope for text.
if let Some(def) = turn_context.subagents_registry.get(&agent_name)
&& let Some(schema) = def.output_schema()
{
// Validate against the subagent's output schema and return
// the JSON body unmodified.
if let Some(def) = turn_context.subagents_registry.get(&agent_name) {
let schema = def.output_schema();
match serde_json::from_str::<serde_json::Value>(&message) {
Ok(val) => {
if let Err(err) =
@@ -2195,24 +2194,14 @@ async fn handle_function_call(
},
}
} else {
// No schema: best-effort envelope
let json_payload = match serde_json::from_str::<serde_json::Value>(&message)
{
Ok(val) => serde_json::json!({
"subagent": agent_name,
"output": val,
}),
Err(_) => serde_json::json!({
"subagent": agent_name,
"output": { "type": "text", "message": message },
}),
};
let content = serde_json::to_string(&json_payload).unwrap_or(message);
ResponseInputItem::FunctionCallOutput {
call_id,
output: FunctionCallOutputPayload {
content,
success: Some(true),
content: format!(
"subagent not found when validating output: {}",
agent_name
),
success: Some(false),
},
}
}

View File

@@ -1,8 +1,8 @@
{
"name": "hello",
"description": "Built-in test subagent that replies with a greeting",
"instructions": "Reply with exactly this text and nothing else: Hello from subagent",
"instructions": "Reply with exactly this text and nothing else, as a JSON string: Hello from subagent",
"output_schema": { "type": "string" },
"tools": [],
"reasoning_effort": "minimal"
}

View File

@@ -20,11 +20,10 @@ pub struct SubagentDefinition {
pub instructions: String,
//TODO: add allowed tools. we inherit the parent agent's tools for now.
/// Optional structured output schema. When set, the subagent must return a
/// single JSON value that validates against this schema. The schema will be
/// embedded into the subagent's instructions so the model can adhere to it.
#[serde(default)]
output_schema: Option<JsonSchema>,
/// Structured output schema. The subagent must return a single JSON value
/// that validates against this schema. The schema will be embedded into the
/// subagent's instructions so the model can adhere to it.
output_schema: JsonSchema,
/// Optional model override for this subagent. When not provided, inherits
/// the parent session's configured model.
@@ -59,7 +58,7 @@ impl SubagentDefinition {
})
}
pub(crate) fn output_schema(&self) -> Option<&JsonSchema> {
self.output_schema.as_ref()
pub(crate) fn output_schema(&self) -> &JsonSchema {
&self.output_schema
}
}

View File

@@ -19,24 +19,20 @@ pub struct RunSubagentArgs {
///
/// For user- and project-scoped subagents, we append their instructions to the
/// parent session's base instructions. For embedded defaults, we use only the
/// subagent's instructions. If an output schema is present, we augment the
/// subagent instructions with strict JSON output requirements.
/// subagent's instructions. We always augment the subagent instructions with
/// strict JSON output requirements based on its schema.
fn compose_base_instructions_for_subagent(
def: &SubagentDefinition,
parent_base_instructions: Option<&str>,
) -> String {
// Start with the subagent's own instructions, optionally augmented with
// structured output requirements.
let child_instructions = if let Some(schema) = def.output_schema() {
let schema_json = serde_json::to_string_pretty(schema).unwrap_or_else(|_| "{}".to_string());
format!(
"{instructions}\n\nOutput format requirements:\n- Reply with a single JSON value that strictly matches the following JSON Schema.\n- Do not include any commentary, markdown, or extra text.\n- Do not include trailing explanations.\n\nSchema:\n{schema_json}\n",
instructions = def.instructions,
schema_json = schema_json
)
} else {
def.instructions.clone()
};
let schema_json =
serde_json::to_string_pretty(def.output_schema()).unwrap_or_else(|_| "{}".to_string());
let child_instructions = format!(
"{}\n\nOutput format requirements:\n- Reply with a single JSON value that strictly matches the following JSON Schema.\n- Do not include any commentary, markdown, or extra text.\n- Do not include trailing explanations.\n\nSchema:\n{}\n",
def.instructions, schema_json
);
match def.source {
SubagentSource::User | SubagentSource::Project => match parent_base_instructions {