jarvis / tests /test_openai_tooling.py
Jonathan Haas
Add LLM memory quality eval gate and promote OpenAI-agent readiness updates
1265f9a
Raw
History Blame Contribute Delete
3.62 kB
from __future__ import annotations
import json
import pytest
from jarvis.tools.openai_tooling import build_function_tool, tool_result_text
def test_tool_result_text_prefers_text_content_and_serializes_remainder() -> None:
result = {
"content": [
{"type": "text", "text": "first"},
{"type": "input_image", "image_url": "https://example.com/x.png"},
{"type": "text", "text": "second"},
],
"meta": {"k": "v"},
}
text = tool_result_text(result)
assert text.startswith("first\nsecond\n")
assert json.loads(text.splitlines()[-1]) == {"meta": {"k": "v"}}
@pytest.mark.asyncio
async def test_build_function_tool_invokes_handler_and_normalizes_schema() -> None:
calls: list[dict[str, object]] = []
async def _handler(args: dict[str, object]) -> dict[str, object]:
calls.append(dict(args))
return {"content": [{"type": "text", "text": "ok"}]}
tool = build_function_tool(
name="demo_tool",
description="demo",
schema={"properties": {"x": {"type": "string"}}, "required": ["x"]},
handler=_handler,
)
text = await tool.on_invoke_tool(None, '{"x":"value"}')
assert text == "ok"
assert calls == [{"x": "value"}]
assert tool.params_json_schema["type"] == "object"
assert tool.params_json_schema["required"] == ["x"]
assert tool.params_json_schema["properties"]["x"]["type"] == "string"
@pytest.mark.asyncio
async def test_build_function_tool_rejects_non_object_args() -> None:
async def _handler(args: dict[str, object]) -> dict[str, object]:
return {"args": args}
tool = build_function_tool(
name="demo_tool",
description="demo",
schema={"type": "object"},
handler=_handler,
)
assert json.loads(await tool.on_invoke_tool(None, "")) == {"args": {}}
assert "expected a JSON object" in (await tool.on_invoke_tool(None, "[]"))
assert "expected a JSON object" in (await tool.on_invoke_tool(None, "{not-json"))
@pytest.mark.asyncio
async def test_build_function_tool_validates_required_types_and_unknown_fields() -> None:
async def _handler(args: dict[str, object]) -> dict[str, object]:
return {"args": args}
tool = build_function_tool(
name="typed_tool",
description="demo",
schema={
"type": "object",
"properties": {
"count": {"type": "integer"},
"flag": {"type": "boolean"},
},
"required": ["count"],
},
handler=_handler,
)
missing = await tool.on_invoke_tool(None, "{}")
assert "missing required field args.count" in missing
bad_type = await tool.on_invoke_tool(None, '{"count":"x"}')
assert "field args.count must be integer" in bad_type
unknown = await tool.on_invoke_tool(None, '{"count":1,"extra":true}')
assert "unexpected field args.extra" in unknown
ok = await tool.on_invoke_tool(None, '{"count":2,"flag":false}')
assert json.loads(ok) == {"args": {"count": 2, "flag": False}}
@pytest.mark.asyncio
async def test_build_function_tool_hides_internal_exception_details() -> None:
async def _handler(_args: dict[str, object]) -> dict[str, object]:
raise RuntimeError("secret payload should not leak")
tool = build_function_tool(
name="sensitive_tool",
description="demo",
schema={"type": "object"},
handler=_handler,
)
text = await tool.on_invoke_tool(None, "{}")
assert text == "Tool sensitive_tool failed."
assert "secret payload" not in text