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.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
extra_body: Optional[JsonValue] = None
|
||||
Reference in New Issue
Block a user