diff --git a/skills/product-data/schemas/export_dataset_records.input.schema.json b/skills/product-data/schemas/export_dataset_records.input.schema.json index 315eda7..2b17b62 100644 --- a/skills/product-data/schemas/export_dataset_records.input.schema.json +++ b/skills/product-data/schemas/export_dataset_records.input.schema.json @@ -1,8 +1,14 @@ { "type": "object", "properties": { - "records": {"type": "array"}, - "records_path": {"type": "string"}, + "records": { + "type": "array", + "description": "Canonical records. Provide exactly one of records or records_path." + }, + "records_path": { + "type": "string", + "description": "Path to records JSON/JSONL. Provide exactly one of records or records_path." + }, "output_path": {"type": "string"}, "output_dir": {"type": "string"}, "output_format": { @@ -13,9 +19,5 @@ "require_validation_ok": {"type": "boolean", "default": true}, "overwrite": {"type": "boolean", "default": true}, "export_table": {"type": "boolean", "default": true} - }, - "anyOf": [ - {"required": ["records"]}, - {"required": ["records_path"]} - ] + } } diff --git a/skills/product-data/schemas/export_dataset_table.input.schema.json b/skills/product-data/schemas/export_dataset_table.input.schema.json index 1d0543c..8db6c10 100644 --- a/skills/product-data/schemas/export_dataset_table.input.schema.json +++ b/skills/product-data/schemas/export_dataset_table.input.schema.json @@ -1,15 +1,17 @@ { "type": "object", "properties": { - "records": {"type": "array"}, - "records_path": {"type": "string"}, + "records": { + "type": "array", + "description": "Canonical records. Provide exactly one of records or records_path." + }, + "records_path": { + "type": "string", + "description": "Path to records JSON/JSONL. Provide exactly one of records or records_path." + }, "output_path": {"type": "string"}, "output_dir": {"type": "string"}, "require_validation_ok": {"type": "boolean", "default": true}, "overwrite": {"type": "boolean", "default": true} - }, - "anyOf": [ - {"required": ["records"]}, - {"required": ["records_path"]} - ] + } } diff --git a/skills/product-data/schemas/normalize_dataset_draft.input.schema.json b/skills/product-data/schemas/normalize_dataset_draft.input.schema.json index c127ab2..f97cb4f 100644 --- a/skills/product-data/schemas/normalize_dataset_draft.input.schema.json +++ b/skills/product-data/schemas/normalize_dataset_draft.input.schema.json @@ -1,8 +1,14 @@ { "type": "object", "properties": { - "draft_text": {"type": "string"}, - "draft_path": {"type": "string"}, + "draft_text": { + "type": "string", + "description": "Dataset draft text. Provide exactly one of draft_text or draft_path." + }, + "draft_path": { + "type": "string", + "description": "Path to UTF-8 dataset draft text. Provide exactly one of draft_text or draft_path." + }, "batch_id": {"type": "string", "default": "aabbccdd"}, "source_type": { "type": "string", @@ -12,9 +18,5 @@ "base_timestamp": {"type": "integer"}, "timestamp_step_ms": {"type": "integer", "minimum": 1, "default": 60000}, "default_request_id": {"type": "string", "default": "aabbccdd"} - }, - "anyOf": [ - {"required": ["draft_text"]}, - {"required": ["draft_path"]} - ] + } } diff --git a/skills/product-data/schemas/validate_dataset_records.input.schema.json b/skills/product-data/schemas/validate_dataset_records.input.schema.json index b93494b..cb2fc00 100644 --- a/skills/product-data/schemas/validate_dataset_records.input.schema.json +++ b/skills/product-data/schemas/validate_dataset_records.input.schema.json @@ -1,11 +1,13 @@ { "type": "object", "properties": { - "records": {"type": "array"}, - "records_path": {"type": "string"} - }, - "anyOf": [ - {"required": ["records"]}, - {"required": ["records_path"]} - ] + "records": { + "type": "array", + "description": "Canonical records. Provide exactly one of records or records_path." + }, + "records_path": { + "type": "string", + "description": "Path to records JSON/JSONL. Provide exactly one of records or records_path." + } + } } diff --git a/src/agent_tool_specs/data_agent.py b/src/agent_tool_specs/data_agent.py index 590150d..dd316cb 100644 --- a/src/agent_tool_specs/data_agent.py +++ b/src/agent_tool_specs/data_agent.py @@ -437,11 +437,11 @@ def build_data_agent_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentToo 'properties': { 'draft_text': { 'type': 'string', - 'description': 'Dataset draft text in dataset draft text v1 format.', + 'description': 'Dataset draft text in dataset draft text v1 format. Provide exactly one of draft_text or draft_path.', }, 'draft_path': { 'type': 'string', - 'description': 'Path to a UTF-8 dataset draft text file. Prefer this for large drafts.', + 'description': 'Path to a UTF-8 dataset draft text file. Prefer this for large drafts. Provide exactly one of draft_text or draft_path.', }, 'batch_id': { 'type': 'string', @@ -469,10 +469,6 @@ def build_data_agent_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentToo 'description': 'Required for generated data; returned by data_agent_confirm_generation_plan.', }, }, - 'anyOf': [ - {'required': ['draft_text']}, - {'required': ['draft_path']}, - ], }, handler=resolve_handler(handlers, 'data_agent_normalize_dataset_draft', 'data-agent'), ), @@ -490,17 +486,13 @@ def build_data_agent_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentToo {'type': 'array'}, {'type': 'string'}, ], - 'description': 'Canonical records as an array, or a JSON string containing the array.', + 'description': 'Canonical records as an array, or a JSON string containing the array. Provide exactly one of records or records_path.', }, 'records_path': { 'type': 'string', - 'description': 'Path to records JSON/JSONL. Prefer this when records are large.', + 'description': 'Path to records JSON/JSONL. Prefer this when records are large. Provide exactly one of records or records_path.', }, }, - 'anyOf': [ - {'required': ['records']}, - {'required': ['records_path']}, - ], }, handler=resolve_handler(handlers, 'data_agent_validate_dataset_records', 'data-agent'), ), @@ -519,11 +511,11 @@ def build_data_agent_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentToo {'type': 'array'}, {'type': 'string'}, ], - 'description': 'Canonical records as an array, or a JSON string containing the array.', + 'description': 'Canonical records as an array, or a JSON string containing the array. Provide exactly one of records or records_path.', }, 'records_path': { 'type': 'string', - 'description': 'Path to records JSON/JSONL. Prefer this when records are large.', + 'description': 'Path to records JSON/JSONL. Prefer this when records are large. Provide exactly one of records or records_path.', }, 'output_path': { 'type': 'string', @@ -552,10 +544,6 @@ def build_data_agent_tools(handlers: Mapping[str, ToolHandler]) -> list[AgentToo }, }, 'required': ['output_path'], - 'anyOf': [ - {'required': ['records']}, - {'required': ['records_path']}, - ], }, handler=resolve_handler(handlers, 'data_agent_export_dataset_records', 'data-agent'), ), diff --git a/tests/test_data_agent_records.py b/tests/test_data_agent_records.py index 4982681..ad73f6c 100644 --- a/tests/test_data_agent_records.py +++ b/tests/test_data_agent_records.py @@ -647,6 +647,17 @@ target: Agent(tag='地图导航') self.assertEqual(export_payload['record_count'], 1) self.assertTrue(table_exists) + def test_tool_schemas_avoid_top_level_composition_keywords(self) -> None: + # 部分 Bedrock/Anthropic 兼容后端不接受顶层 anyOf/oneOf/allOf。 + blocked_keywords = {'anyOf', 'oneOf', 'allOf'} + violations = [ + name + for name, tool in default_tool_registry().items() + if blocked_keywords.intersection(tool.parameters) + ] + + self.assertEqual(violations, []) + def test_data_agent_tools_route_relative_outputs_to_session_output(self) -> None: with tempfile.TemporaryDirectory() as tmp_dir: root = Path(tmp_dir)