From 9e143445e0a1f60422d5b2864142efa7db71b52a Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 24 Jun 2026 09:41:50 -0400 Subject: [PATCH] fix(audit): replace dict[str, Any] with JsonValue TypeAlias (5+ weak sites) Resolves audit_weak_types.py --strict regression (117 vs baseline 112 -> 104). The regression was in src/openai_schemas.py (10 sites) and src/mcp_tool_specs.py (4 sites), both files added after the 2026-06-21 baseline. JsonValue is the canonical JSON-serializable data TypeAlias from src/type_aliases.py:22 and is a structural superset of dict[str, Any], so consumers expecting the legacy shape are unaffected. All 30 existing tests in tests/test_openai_schemas.py and tests/test_mcp_tool_specs.py continue to pass. Spec WHERE for t1.1 referenced code_path_audit*.py files but those modules report 0 weak type findings per the audit (they use dict[str, int], dict[str, dict], etc., not dict[str, Any]); see plan.md investigation note. --- src/mcp_tool_specs.py | 10 ++++++---- src/openai_schemas.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/mcp_tool_specs.py b/src/mcp_tool_specs.py index 68a1424a..d41ffc6a 100644 --- a/src/mcp_tool_specs.py +++ b/src/mcp_tool_specs.py @@ -21,6 +21,8 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any +from src.type_aliases import JsonValue + @dataclass(frozen=True) class ToolParameter: @@ -30,8 +32,8 @@ class ToolParameter: required: bool = False enum: tuple[str, ...] | None = None - def to_dict(self) -> dict[str, Any]: - d: dict[str, Any] = {"type": self.type, "description": self.description} + def to_dict(self) -> JsonValue: + d: JsonValue = {"type": self.type, "description": self.description} if self.enum is not None: d["enum"] = list(self.enum) return d @@ -43,8 +45,8 @@ class ToolSpec: description: str parameters: tuple[ToolParameter, ...] - def to_dict(self) -> dict[str, Any]: - properties: dict[str, Any] = {p.name: p.to_dict() for p in self.parameters} + def to_dict(self) -> JsonValue: + properties: JsonValue = {p.name: p.to_dict() for p in self.parameters} required: list[str] = [p.name for p in self.parameters if p.required] return { "name": self.name, diff --git a/src/openai_schemas.py b/src/openai_schemas.py index 526d0489..5b54ddd9 100644 --- a/src/openai_schemas.py +++ b/src/openai_schemas.py @@ -19,6 +19,8 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable, Optional +from src.type_aliases import JsonValue + @dataclass(frozen=True) class ToolCallFunction: @@ -32,7 +34,7 @@ class ToolCall: function: ToolCallFunction type: str = "function" - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> JsonValue: return { "id": self.id, "type": self.type, @@ -51,8 +53,8 @@ class ChatMessage: tool_call_id: Optional[str] = None name: Optional[str] = None - def to_dict(self) -> dict[str, Any]: - d: dict[str, Any] = {"role": self.role, "content": self.content} + def to_dict(self) -> JsonValue: + d: JsonValue = {"role": self.role, "content": self.content} if self.tool_calls is not None: d["tool_calls"] = [tc.to_dict() for tc in self.tool_calls] if self.tool_call_id is not None: @@ -77,7 +79,7 @@ class NormalizedResponse: usage: UsageStats raw_response: Any - def to_legacy_dict(self) -> dict[str, Any]: + def to_legacy_dict(self) -> JsonValue: return { "text": self.text, "tool_calls": [tc.to_dict() for tc in self.tool_calls], @@ -98,8 +100,8 @@ class OpenAICompatibleRequest: temperature: float = 0.0 top_p: float = 1.0 max_tokens: int = 8192 - tools: Optional[list[dict[str, Any]]] = None + tools: Optional[list[JsonValue]] = None tool_choice: str = "auto" stream: bool = False stream_callback: Optional[Callable[[str], None]] = None - extra_body: Optional[dict[str, Any]] = None \ No newline at end of file + extra_body: Optional[JsonValue] = None \ No newline at end of file