diff --git a/scripts/release.py b/scripts/release.py index 14a6c0332e..68cbb168e4 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -51,6 +51,7 @@ AUTHOR_MAP = { "piyushvp1@gmail.com": "thelumiereguy", "harish.kukreja@gmail.com": "counterposition", "cleo@edaphic.xyz": "curiouscleo", + "hirokazu.ogawa@kwansei.ac.jp": "hrkzogw", "127238744+teknium1@users.noreply.github.com": "teknium1", "128259593+Gutslabs@users.noreply.github.com": "Gutslabs", "50326054+nocturnum91@users.noreply.github.com": "nocturnum91", diff --git a/tests/tools/test_memory_tool_schema.py b/tests/tools/test_memory_tool_schema.py index ea5ebdea5e..3129674bcf 100644 --- a/tests/tools/test_memory_tool_schema.py +++ b/tests/tools/test_memory_tool_schema.py @@ -1,38 +1,48 @@ +"""Schema-shape tests for the built-in memory tool. + +The memory tool previously used ``allOf: [{if: ..., then: {required: ...}}]`` +at the top level of ``parameters`` to hint per-action required fields. That +form was: + + 1. Ignored by every provider (Chat Completions doesn't honour ``if/then`` + on function schemas), so it never actually enforced anything. + 2. **Rejected outright by strict backends** — OpenAI's Codex endpoint + (``chatgpt.com/backend-api/codex``, gpt-5.x) returns + ``Invalid schema for function 'memory': schema must have type 'object' + and not have 'oneOf'/'anyOf'/'allOf'/'enum'/'not' at the top level``. + +We now rely on the runtime handler (``memory_tool()`` in ``tools/memory_tool.py``) +to validate required fields per action and return actionable error messages. +These tests guard the schema against regressing back to a shape strict +backends reject. +""" + import json + from tools.memory_tool import MEMORY_SCHEMA -def test_memory_schema_requires_content_and_old_text_for_replace_action(): - schema = MEMORY_SCHEMA["parameters"] - assert schema["required"] == ["action", "target"] - - all_of = schema.get("allOf") - assert all_of, "memory schema should use conditional requirements" - - replace_requirements = [ - branch["then"].get("required", []) - for branch in all_of - if branch.get("if", {}).get("properties", {}).get("action", {}).get("const") == "replace" - ] - assert replace_requirements == [["old_text", "content"]] +_FORBIDDEN_TOP_LEVEL_KEYS = ("allOf", "anyOf", "oneOf", "enum", "not") -def test_memory_schema_requires_content_for_add_action(): - add_requirements = [ - branch["then"].get("required", []) - for branch in MEMORY_SCHEMA["parameters"].get("allOf", []) - if branch.get("if", {}).get("properties", {}).get("action", {}).get("const") == "add" - ] - assert add_requirements == [["content"]] +def test_memory_schema_has_no_forbidden_top_level_combinators(): + """OpenAI's Codex backend rejects these at the top level of parameters.""" + params = MEMORY_SCHEMA["parameters"] + for key in _FORBIDDEN_TOP_LEVEL_KEYS: + assert key not in params, ( + f"top-level {key!r} in memory tool parameters will break the " + "Codex backend (chatgpt.com/backend-api/codex). Per-action " + "required-field checks belong in the runtime handler, not the schema." + ) -def test_memory_schema_requires_old_text_for_remove_action(): - remove_requirements = [ - branch["then"].get("required", []) - for branch in MEMORY_SCHEMA["parameters"].get("allOf", []) - if branch.get("if", {}).get("properties", {}).get("action", {}).get("const") == "remove" - ] - assert remove_requirements == [["old_text"]] +def test_memory_schema_is_well_formed(): + params = MEMORY_SCHEMA["parameters"] + assert params["type"] == "object" + assert params["required"] == ["action", "target"] + # Nested ``enum`` on property values is fine — only top-level is forbidden. + assert params["properties"]["action"]["enum"] == ["add", "replace", "remove"] + assert params["properties"]["target"]["enum"] == ["memory", "user"] def test_memory_schema_is_json_serializable(): diff --git a/tools/memory_tool.py b/tools/memory_tool.py index 8dc9b20ab3..0de12a64f3 100644 --- a/tools/memory_tool.py +++ b/tools/memory_tool.py @@ -560,29 +560,6 @@ MEMORY_SCHEMA = { }, }, "required": ["action", "target"], - "allOf": [ - { - "if": { - "properties": {"action": {"const": "add"}}, - "required": ["action"], - }, - "then": {"required": ["content"]}, - }, - { - "if": { - "properties": {"action": {"const": "replace"}}, - "required": ["action"], - }, - "then": {"required": ["old_text", "content"]}, - }, - { - "if": { - "properties": {"action": {"const": "remove"}}, - "required": ["action"], - }, - "then": {"required": ["old_text"]}, - }, - ], }, }