Private
Public Access
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 dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from src.type_aliases import JsonValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ToolParameter:
|
class ToolParameter:
|
||||||
@@ -30,8 +32,8 @@ class ToolParameter:
|
|||||||
required: bool = False
|
required: bool = False
|
||||||
enum: tuple[str, ...] | None = None
|
enum: tuple[str, ...] | None = None
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> JsonValue:
|
||||||
d: dict[str, Any] = {"type": self.type, "description": self.description}
|
d: JsonValue = {"type": self.type, "description": self.description}
|
||||||
if self.enum is not None:
|
if self.enum is not None:
|
||||||
d["enum"] = list(self.enum)
|
d["enum"] = list(self.enum)
|
||||||
return d
|
return d
|
||||||
@@ -43,8 +45,8 @@ class ToolSpec:
|
|||||||
description: str
|
description: str
|
||||||
parameters: tuple[ToolParameter, ...]
|
parameters: tuple[ToolParameter, ...]
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> JsonValue:
|
||||||
properties: dict[str, Any] = {p.name: p.to_dict() for p in self.parameters}
|
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]
|
required: list[str] = [p.name for p in self.parameters if p.required]
|
||||||
return {
|
return {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
|
from src.type_aliases import JsonValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ToolCallFunction:
|
class ToolCallFunction:
|
||||||
@@ -32,7 +34,7 @@ class ToolCall:
|
|||||||
function: ToolCallFunction
|
function: ToolCallFunction
|
||||||
type: str = "function"
|
type: str = "function"
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> JsonValue:
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"type": self.type,
|
"type": self.type,
|
||||||
@@ -51,8 +53,8 @@ class ChatMessage:
|
|||||||
tool_call_id: Optional[str] = None
|
tool_call_id: Optional[str] = None
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> JsonValue:
|
||||||
d: dict[str, Any] = {"role": self.role, "content": self.content}
|
d: JsonValue = {"role": self.role, "content": self.content}
|
||||||
if self.tool_calls is not None:
|
if self.tool_calls is not None:
|
||||||
d["tool_calls"] = [tc.to_dict() for tc in self.tool_calls]
|
d["tool_calls"] = [tc.to_dict() for tc in self.tool_calls]
|
||||||
if self.tool_call_id is not None:
|
if self.tool_call_id is not None:
|
||||||
@@ -77,7 +79,7 @@ class NormalizedResponse:
|
|||||||
usage: UsageStats
|
usage: UsageStats
|
||||||
raw_response: Any
|
raw_response: Any
|
||||||
|
|
||||||
def to_legacy_dict(self) -> dict[str, Any]:
|
def to_legacy_dict(self) -> JsonValue:
|
||||||
return {
|
return {
|
||||||
"text": self.text,
|
"text": self.text,
|
||||||
"tool_calls": [tc.to_dict() for tc in self.tool_calls],
|
"tool_calls": [tc.to_dict() for tc in self.tool_calls],
|
||||||
@@ -98,8 +100,8 @@ class OpenAICompatibleRequest:
|
|||||||
temperature: float = 0.0
|
temperature: float = 0.0
|
||||||
top_p: float = 1.0
|
top_p: float = 1.0
|
||||||
max_tokens: int = 8192
|
max_tokens: int = 8192
|
||||||
tools: Optional[list[dict[str, Any]]] = None
|
tools: Optional[list[JsonValue]] = None
|
||||||
tool_choice: str = "auto"
|
tool_choice: str = "auto"
|
||||||
stream: bool = False
|
stream: bool = False
|
||||||
stream_callback: Optional[Callable[[str], None]] = None
|
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