art agent: enforce json schema
This commit is contained in:
@@ -88,6 +88,7 @@ impl Tool for EditImageTool {
|
||||
"type": "string",
|
||||
"description": "编辑提示词,描述希望如何修改图片。例如「把背景换成红色」、「把人物改成坐着」。"
|
||||
},
|
||||
// TODO need to introduce size param, but that needs more metadata such as original image size, skip in this version
|
||||
"model": {
|
||||
"type": "string",
|
||||
"enum": [GPT_IMAGE_2_MODEL],
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::agent::tools::generate_image::{
|
||||
};
|
||||
use crate::agent::tools::image_generation_options::{
|
||||
default_image_aspect_ratio, default_image_model, default_image_size,
|
||||
image_aspect_ratio_parameter_schema, image_model_parameter_schema, image_size_parameter_schema,
|
||||
image_aspect_ratio_parameter_schema, image_model_parameter_schema,
|
||||
image_model_size_constraint_schema, image_size_parameter_schema,
|
||||
validate_image_generation_options,
|
||||
};
|
||||
use crate::framework::tool::ToolFailureKind;
|
||||
@@ -53,7 +54,8 @@ impl Tool for GenerateCharacterTool {
|
||||
"image_size": image_size_parameter_schema(),
|
||||
},
|
||||
"required": ["prompt"],
|
||||
"additionalProperties": false
|
||||
"additionalProperties": false,
|
||||
"allOf": [image_model_size_constraint_schema()]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::agent::tools::context::EditorToolContext;
|
||||
use crate::agent::tools::image_generation_options::{
|
||||
ImageGenerationOptionsError, default_image_aspect_ratio, default_image_model,
|
||||
default_image_size, image_aspect_ratio_parameter_schema, image_model_parameter_schema,
|
||||
image_size_parameter_schema, validate_image_generation_options,
|
||||
image_model_size_constraint_schema, image_size_parameter_schema,
|
||||
validate_image_generation_options,
|
||||
};
|
||||
use crate::framework::tool::{Tool, ToolFailure, ToolFailureKind};
|
||||
use platform_image::{GPT_IMAGE_2_MODEL, NANOBANANA_2_MODEL};
|
||||
@@ -133,7 +134,8 @@ impl Tool for GenerateIconSpritesheetTool {
|
||||
"image_size": image_size_parameter_schema()
|
||||
},
|
||||
"required": ["reference_image_id", "icon_descriptions"],
|
||||
"additionalProperties": false
|
||||
"additionalProperties": false,
|
||||
"allOf": [image_model_size_constraint_schema()]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::agent::tools::context::EditorToolContext;
|
||||
use crate::agent::tools::image_generation_options::{
|
||||
ImageGenerationOptionsError, default_image_aspect_ratio, default_image_model,
|
||||
default_image_size, image_aspect_ratio_parameter_schema, image_model_parameter_schema,
|
||||
image_size_parameter_schema, validate_image_generation_options,
|
||||
image_model_size_constraint_schema, image_size_parameter_schema,
|
||||
validate_image_generation_options,
|
||||
};
|
||||
use crate::framework::tool::{Tool, ToolFailure, ToolFailureKind};
|
||||
use platform_image::{GPT_IMAGE_2_MODEL, NANOBANANA_2_MODEL};
|
||||
@@ -113,7 +114,8 @@ impl Tool for GenerateImageTool {
|
||||
"image_size": image_size_parameter_schema()
|
||||
},
|
||||
"required": ["prompt"],
|
||||
"additionalProperties": false
|
||||
"additionalProperties": false,
|
||||
"allOf": [image_model_size_constraint_schema()]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::agent::tools::generate_image::{
|
||||
EditorImageGenerationResult, GenerateImageError, GenerateImageToolOutput,
|
||||
};
|
||||
use crate::agent::tools::image_generation_options::{
|
||||
default_image_aspect_ratio, default_image_size, image_aspect_ratio_parameter_schema,
|
||||
image_size_parameter_schema, validate_image_generation_options,
|
||||
default_image_aspect_ratio, default_image_size, gpt_image_2_size_parameter_schema,
|
||||
image_aspect_ratio_parameter_schema, validate_image_generation_options,
|
||||
};
|
||||
use crate::framework::tool::ToolFailureKind;
|
||||
use crate::framework::tool::{Tool, ToolFailure};
|
||||
@@ -58,7 +58,7 @@ impl Tool for GenerateUiDesignTool {
|
||||
},
|
||||
"reference_image_ids": { "type": "array", "items": { "type": "string" }, "description": "image_id(s) for desc UI 风格或布局" },
|
||||
"aspect_ratio": image_aspect_ratio_parameter_schema(),
|
||||
"image_size": image_size_parameter_schema()
|
||||
"image_size": gpt_image_2_size_parameter_schema()
|
||||
},
|
||||
"required": ["prompt", "model"],
|
||||
"additionalProperties": false
|
||||
@@ -169,6 +169,14 @@ mod tests {
|
||||
parameters["properties"]["model"]["default"],
|
||||
GPT_IMAGE_2_MODEL
|
||||
);
|
||||
assert_eq!(
|
||||
parameters["properties"]["image_size"]["enum"],
|
||||
json!(["1K", "2K"])
|
||||
);
|
||||
assert_eq!(
|
||||
parameters["properties"]["image_size"]["default"],
|
||||
json!("1K")
|
||||
);
|
||||
assert!(
|
||||
parameters["required"]
|
||||
.as_array()
|
||||
|
||||
@@ -125,9 +125,10 @@ impl Tool for GenerateVideoTool {
|
||||
"additionalProperties": false,
|
||||
"allOf": [
|
||||
{
|
||||
// model 省略时运行时会补为 fast;这里故意不要求 model 存在,
|
||||
// 让 JSON Schema 对省略 model 的调用应用同一分辨率约束。
|
||||
"if": {
|
||||
"properties": { "model": { "const": GenerateVideoTool::SEEDANCE_2_FAST_MODEL } },
|
||||
"required": ["model"]
|
||||
"properties": { "model": { "const": GenerateVideoTool::SEEDANCE_2_FAST_MODEL } }
|
||||
},
|
||||
"then": {
|
||||
"properties": { "resolution": { "enum": GenerateVideoTool::SEEDANCE_2_FAST_RESOLUTIONS } }
|
||||
@@ -360,5 +361,15 @@ mod tests {
|
||||
);
|
||||
assert_eq!(schema["properties"]["sound"]["default"], json!("on"));
|
||||
assert_eq!(schema["allOf"].as_array().map(Vec::len), Some(2));
|
||||
let fast_model_condition = &schema["allOf"][0];
|
||||
assert_eq!(
|
||||
fast_model_condition["if"]["properties"]["model"]["const"],
|
||||
json!(GenerateVideoTool::DEFAULT_VIDEO_MODEL)
|
||||
);
|
||||
assert!(fast_model_condition["if"].get("required").is_none());
|
||||
assert_eq!(
|
||||
fast_model_condition["then"]["properties"]["resolution"]["enum"],
|
||||
json!(["480p", "720p"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +93,36 @@ pub fn image_aspect_ratio_parameter_schema() -> Value {
|
||||
pub fn image_size_parameter_schema() -> Value {
|
||||
json!({
|
||||
"type": "string",
|
||||
"enum": ["0.5K", "1K", "2K"],
|
||||
"enum": NANOBANANA_2_IMAGE_SIZES,
|
||||
"default": DEFAULT_IMAGE_SIZE,
|
||||
"description": "图片尺寸档位。nanobanana2 支持 0.5K、1K、2K;gpt-image-2 仅支持 1K、2K;默认 1K。"
|
||||
})
|
||||
}
|
||||
|
||||
pub fn gpt_image_2_size_parameter_schema() -> Value {
|
||||
json!({
|
||||
"type": "string",
|
||||
"enum": GPT_IMAGE_2_IMAGE_SIZES,
|
||||
"default": DEFAULT_IMAGE_SIZE,
|
||||
"description": "图片尺寸档位。gpt-image-2 仅支持 1K、2K;默认 1K。"
|
||||
})
|
||||
}
|
||||
|
||||
pub fn image_model_size_constraint_schema() -> Value {
|
||||
json!({
|
||||
"if": {
|
||||
"properties": { "model": { "const": GPT_IMAGE_2_MODEL } },
|
||||
// model 省略时运行时默认 nanobanana2,仍允许 0.5K。
|
||||
"required": ["model"]
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"image_size": { "enum": GPT_IMAGE_2_IMAGE_SIZES }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn supported_image_sizes(model: &str) -> Option<&'static [&'static str]> {
|
||||
match model {
|
||||
NANOBANANA_2_MODEL => Some(NANOBANANA_2_IMAGE_SIZES),
|
||||
@@ -157,5 +181,23 @@ mod tests {
|
||||
json!(["0.5K", "1K", "2K"])
|
||||
);
|
||||
assert_eq!(image_size_parameter_schema()["default"], DEFAULT_IMAGE_SIZE);
|
||||
assert_eq!(
|
||||
gpt_image_2_size_parameter_schema()["enum"],
|
||||
json!(["1K", "2K"])
|
||||
);
|
||||
assert_eq!(
|
||||
gpt_image_2_size_parameter_schema()["default"],
|
||||
DEFAULT_IMAGE_SIZE
|
||||
);
|
||||
let model_size_constraint = image_model_size_constraint_schema();
|
||||
assert_eq!(
|
||||
model_size_constraint["if"]["properties"]["model"]["const"],
|
||||
GPT_IMAGE_2_MODEL
|
||||
);
|
||||
assert_eq!(model_size_constraint["if"]["required"], json!(["model"]));
|
||||
assert_eq!(
|
||||
model_size_constraint["then"]["properties"]["image_size"]["enum"],
|
||||
json!(["1K", "2K"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ mod tests {
|
||||
GenerateBackgroundMusicTool, GenerateBackgroundMusicToolArgs,
|
||||
};
|
||||
use super::generate_character::{GenerateCharacterTool, GenerateCharacterToolArgs};
|
||||
use super::generate_icon_spritesheet::GenerateIconSpritesheetToolArgs;
|
||||
use super::generate_icon_spritesheet::{
|
||||
GenerateIconSpritesheetTool, GenerateIconSpritesheetToolArgs,
|
||||
};
|
||||
use super::generate_image::{GenerateImageError, GenerateImageTool, GenerateImageToolArgs};
|
||||
use super::generate_sound_effect::{GenerateSoundEffectTool, GenerateSoundEffectToolArgs};
|
||||
use super::generate_ui_design::{GenerateUiDesignTool, GenerateUiDesignToolArgs};
|
||||
@@ -188,6 +190,18 @@ mod tests {
|
||||
#[test]
|
||||
fn confirmation_schemas_expose_all_finite_choices_as_enums() {
|
||||
let context = EditorToolContext::default();
|
||||
let image = GenerateImageTool {
|
||||
context: context.clone(),
|
||||
}
|
||||
.parameters();
|
||||
let character = GenerateCharacterTool {
|
||||
context: context.clone(),
|
||||
}
|
||||
.parameters();
|
||||
let icon = GenerateIconSpritesheetTool {
|
||||
context: context.clone(),
|
||||
}
|
||||
.parameters();
|
||||
let edit = EditImageTool {
|
||||
context: context.clone(),
|
||||
}
|
||||
@@ -218,5 +232,16 @@ mod tests {
|
||||
assert_eq!(sound["properties"]["duration"]["default"], json!(5));
|
||||
assert_eq!(video["properties"]["sound"]["default"], json!("on"));
|
||||
assert!(music["properties"].get("make_instrumental").is_none());
|
||||
|
||||
for schema in [&image, &character, &icon] {
|
||||
assert_eq!(
|
||||
schema["allOf"][0]["if"]["properties"]["model"]["const"],
|
||||
json!(GPT_IMAGE_2_MODEL)
|
||||
);
|
||||
assert_eq!(
|
||||
schema["allOf"][0]["then"]["properties"]["image_size"]["enum"],
|
||||
json!(["1K", "2K"])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user