mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 04:30:20 +08:00
549 lines
18 KiB
Python
549 lines
18 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the provider schema field coverage matrix.
|
|
|
|
The input inventory is docs/api/provider-interface-definitions.md. Existing
|
|
coverage rows are reused so audited status/notes survive regeneration. Newly
|
|
introduced provider fields get conservative same-format/native and cross-format
|
|
fail-closed defaults until a human audits whether they deserve an explicit
|
|
mapping.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import dataclasses
|
|
from collections import Counter, defaultdict
|
|
from pathlib import Path
|
|
from typing import Iterable
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
DEFAULT_DEFINITIONS = ROOT / "docs/api/provider-interface-definitions.md"
|
|
DEFAULT_MATRIX = ROOT / "docs/api/format-field-coverage-matrix.md"
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
class SourceField:
|
|
provider: str
|
|
schema: str
|
|
field: str
|
|
required: str
|
|
field_type: str
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
class CoverageStatus:
|
|
surface: str
|
|
same_format_runtime: str
|
|
canonical_roundtrip: str
|
|
cross_format: str
|
|
notes: str
|
|
|
|
|
|
OPENAI_CHAT_MAPPED = {
|
|
"model",
|
|
"messages",
|
|
"max_tokens",
|
|
"max_completion_tokens",
|
|
"temperature",
|
|
"top_p",
|
|
"top_logprobs",
|
|
"tools",
|
|
"tool_choice",
|
|
"parallel_tool_calls",
|
|
"metadata",
|
|
"response_format",
|
|
"reasoning_effort",
|
|
"verbosity",
|
|
"store",
|
|
"service_tier",
|
|
"safety_identifier",
|
|
"prompt_cache_key",
|
|
"prompt_cache_retention",
|
|
"stream",
|
|
}
|
|
|
|
OPENAI_CHAT_BLOCKED = {
|
|
"n",
|
|
"stop",
|
|
"presence_penalty",
|
|
"frequency_penalty",
|
|
"seed",
|
|
"logprobs",
|
|
"stream_options",
|
|
"user",
|
|
"function_call",
|
|
"functions",
|
|
"logit_bias",
|
|
"modalities",
|
|
"prediction",
|
|
"audio",
|
|
"web_search_options",
|
|
}
|
|
|
|
OPENAI_RESPONSES_MAPPED = {
|
|
"model",
|
|
"input",
|
|
"instructions",
|
|
"max_output_tokens",
|
|
"temperature",
|
|
"top_p",
|
|
"top_logprobs",
|
|
"metadata",
|
|
"parallel_tool_calls",
|
|
"text",
|
|
"tools",
|
|
"tool_choice",
|
|
"reasoning",
|
|
"store",
|
|
"service_tier",
|
|
"safety_identifier",
|
|
"prompt_cache_key",
|
|
"prompt_cache_retention",
|
|
}
|
|
|
|
OPENAI_RESPONSES_BLOCKED = {
|
|
"include",
|
|
"previous_response_id",
|
|
"truncation",
|
|
"prompt",
|
|
"conversation",
|
|
"background",
|
|
"max_tool_calls",
|
|
"user",
|
|
"context_management",
|
|
"stream",
|
|
"stream_options",
|
|
}
|
|
|
|
CLAUDE_MAPPED_FIELDS = {
|
|
"id",
|
|
"type",
|
|
"role",
|
|
"text",
|
|
"content",
|
|
"source",
|
|
"name",
|
|
"description",
|
|
"input",
|
|
"input_schema",
|
|
"messages",
|
|
"model",
|
|
"max_tokens",
|
|
"system",
|
|
"temperature",
|
|
"top_p",
|
|
"top_k",
|
|
"stop_sequences",
|
|
"tool_choice",
|
|
"tools",
|
|
"metadata",
|
|
"thinking",
|
|
"output_config",
|
|
"usage",
|
|
"stop_reason",
|
|
"stop_sequence",
|
|
}
|
|
|
|
CLAUDE_PROVIDER_ONLY_FIELDS = {
|
|
"cache_control",
|
|
"container",
|
|
"inference_geo",
|
|
"service_tier",
|
|
"allowed_callers",
|
|
"allowed_domains",
|
|
"blocked_domains",
|
|
"defer_loading",
|
|
"max_uses",
|
|
"strict",
|
|
"user_location",
|
|
"citations",
|
|
"context",
|
|
"title",
|
|
"file_id",
|
|
"document_index",
|
|
"document_title",
|
|
"cited_text",
|
|
"caller",
|
|
}
|
|
|
|
|
|
def split_markdown_row(line: str) -> list[str]:
|
|
cells: list[str] = []
|
|
current: list[str] = []
|
|
escaped = False
|
|
for char in line:
|
|
if char == "|" and not escaped:
|
|
cells.append("".join(current).strip())
|
|
current.clear()
|
|
else:
|
|
current.append(char)
|
|
escaped = char == "\\" and not escaped
|
|
if escaped and char != "\\":
|
|
escaped = False
|
|
cells.append("".join(current).strip())
|
|
return cells
|
|
|
|
|
|
def strip_markdown_code(value: str) -> str:
|
|
value = value.strip()
|
|
if value.startswith("`") and value.endswith("`"):
|
|
value = value[1:-1]
|
|
return value.replace("\\|", "|")
|
|
|
|
|
|
def escape_markdown_cell(value: str) -> str:
|
|
return value.replace("|", "\\|")
|
|
|
|
|
|
def parse_schema_heading(line: str) -> str | None:
|
|
if not line.startswith("### `"):
|
|
return None
|
|
rest = line[len("### `") :]
|
|
schema, _, _ = rest.partition("`")
|
|
return schema or None
|
|
|
|
|
|
def parse_provider_definition_fields(definitions: str) -> list[SourceField]:
|
|
provider: str | None = None
|
|
schema: str | None = None
|
|
fields: list[SourceField] = []
|
|
|
|
for line in definitions.splitlines():
|
|
if line.startswith("## "):
|
|
if "OpenAI Schema" in line:
|
|
provider = "OpenAI"
|
|
elif "Claude / Anthropic TypeScript" in line:
|
|
provider = "Claude"
|
|
elif "Gemini Schema" in line:
|
|
provider = "Gemini"
|
|
else:
|
|
provider = None
|
|
schema = None
|
|
continue
|
|
|
|
if provider is None:
|
|
continue
|
|
|
|
if heading := parse_schema_heading(line):
|
|
schema = heading
|
|
continue
|
|
|
|
if schema is None or not line.startswith("| `"):
|
|
continue
|
|
|
|
cells = split_markdown_row(line)
|
|
if len(cells) < 4 or cells[2] not in {"是", "否"}:
|
|
continue
|
|
fields.append(
|
|
SourceField(
|
|
provider=provider,
|
|
schema=schema,
|
|
field=strip_markdown_code(cells[1]),
|
|
required=cells[2],
|
|
field_type=strip_markdown_code(cells[3]),
|
|
)
|
|
)
|
|
|
|
return fields
|
|
|
|
|
|
def parse_existing_coverage(
|
|
matrix: str,
|
|
) -> tuple[dict[tuple[str, str, str], CoverageStatus], dict[tuple[str, str], list[CoverageStatus]]]:
|
|
existing: dict[tuple[str, str, str], CoverageStatus] = {}
|
|
profiles: dict[tuple[str, str], list[CoverageStatus]] = defaultdict(list)
|
|
|
|
for line in matrix.splitlines():
|
|
if not line.startswith("| "):
|
|
continue
|
|
cells = split_markdown_row(line)
|
|
if len(cells) < 11 or cells[1] not in {"OpenAI", "Claude", "Gemini"}:
|
|
continue
|
|
status = CoverageStatus(
|
|
surface=cells[6],
|
|
same_format_runtime=cells[7],
|
|
canonical_roundtrip=cells[8],
|
|
cross_format=cells[9],
|
|
notes=cells[10],
|
|
)
|
|
provider = cells[1]
|
|
schema = strip_markdown_code(cells[2])
|
|
field = strip_markdown_code(cells[3])
|
|
existing[(provider, schema, field)] = status
|
|
profiles[(provider, schema)].append(status)
|
|
|
|
return existing, profiles
|
|
|
|
|
|
def most_common(values: Iterable[str]) -> str | None:
|
|
values = list(values)
|
|
if not values:
|
|
return None
|
|
return Counter(values).most_common(1)[0][0]
|
|
|
|
|
|
def openai_surface(schema: str) -> str:
|
|
if "CreateChatCompletion" in schema or "ChatCompletion" in schema:
|
|
return "openai:chat standard"
|
|
if "CreateEmbedding" in schema or "Embedding" in schema:
|
|
return "openai:embedding"
|
|
if any(token in schema for token in ("CreateImage", "EditImage", "Image", "Images")):
|
|
return "openai:image native-only"
|
|
if any(token in schema for token in ("Compact", "Compaction")):
|
|
return "openai:responses:compact native-only"
|
|
if any(
|
|
token in schema
|
|
for token in (
|
|
"Response",
|
|
"Input",
|
|
"Output",
|
|
"Tool",
|
|
"Reasoning",
|
|
"WebSearch",
|
|
"FileSearch",
|
|
"Computer",
|
|
"MCP",
|
|
"CodeInterpreter",
|
|
"Function",
|
|
"Custom",
|
|
"EasyInput",
|
|
"Prompt",
|
|
"Conversation",
|
|
"Annotation",
|
|
"Citation",
|
|
"LogProb",
|
|
"TopLogProb",
|
|
"Metadata",
|
|
"ServiceTier",
|
|
"Verbosity",
|
|
"TextResponse",
|
|
"ResponseFormat",
|
|
"Include",
|
|
"Modalities",
|
|
"ParallelToolCalls",
|
|
"StopConfiguration",
|
|
)
|
|
):
|
|
return "openai:responses standard"
|
|
return "openai auxiliary / not-in-conversion-surface"
|
|
|
|
|
|
def openai_default_status(field: SourceField, profile: list[CoverageStatus]) -> CoverageStatus:
|
|
surface = most_common(status.surface for status in profile) or openai_surface(field.schema)
|
|
if "not-in-conversion-surface" in surface or "native-only" in surface:
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="not-in-conversion-surface",
|
|
cross_format="not-in-conversion-surface",
|
|
notes="not part of current canonical cross-format conversion; same-format runtime path remains provider-native when routed directly",
|
|
)
|
|
if field.schema == "CreateChatCompletionRequest":
|
|
if field.field in OPENAI_CHAT_MAPPED:
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="mapped",
|
|
cross_format="mapped",
|
|
notes="Chat request field maps provider-specifically; target-incompatible cases fail closed",
|
|
)
|
|
if field.field in OPENAI_CHAT_BLOCKED:
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="Chat-only or provider-specific field has no audited lossless target equivalent",
|
|
)
|
|
if field.schema == "CreateResponse":
|
|
if field.field in OPENAI_RESPONSES_MAPPED:
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="mapped",
|
|
cross_format="mapped",
|
|
notes="Responses request field maps provider-specifically; target-incompatible cases fail closed",
|
|
)
|
|
if field.field in OPENAI_RESPONSES_BLOCKED:
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="Responses-only field has no audited lossless Chat/Claude/Gemini target equivalent",
|
|
)
|
|
if profile:
|
|
cross_format = most_common(status.cross_format for status in profile) or "lossy-blocked"
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip=most_common(status.canonical_roundtrip for status in profile)
|
|
or "extension-preserved",
|
|
cross_format=cross_format,
|
|
notes=next(
|
|
(status.notes for status in profile if status.cross_format == cross_format),
|
|
"schema-level handling inherited from audited sibling fields",
|
|
),
|
|
)
|
|
return CoverageStatus(
|
|
surface=surface,
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="OpenAI documented field is preserved same-format; cross-format requires explicit target mapping or fails closed",
|
|
)
|
|
|
|
|
|
def claude_default_status(field: SourceField) -> CoverageStatus:
|
|
if "CountTokens" in field.schema:
|
|
return CoverageStatus(
|
|
surface="claude:messages/count_tokens native-only",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="not-in-conversion-surface",
|
|
cross_format="not-in-conversion-surface",
|
|
notes="count_tokens schemas are provider-native and outside canonical generation conversion",
|
|
)
|
|
if field.field in CLAUDE_MAPPED_FIELDS:
|
|
return CoverageStatus(
|
|
surface="claude:messages standard",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="mapped",
|
|
cross_format="mapped/lossy-blocked",
|
|
notes="Claude field maps where canonical and target support an equivalent; otherwise conversion fails closed",
|
|
)
|
|
if (
|
|
field.field in CLAUDE_PROVIDER_ONLY_FIELDS
|
|
or field.field.endswith("_tokens_details")
|
|
or "cache" in field.field
|
|
):
|
|
return CoverageStatus(
|
|
surface="claude:messages standard",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="Claude provider-specific field is preserved same-format and blocked cross-format without an audited target equivalent",
|
|
)
|
|
return CoverageStatus(
|
|
surface="claude:messages standard",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="Claude nested/provider-specific field is same-format preserved; cross-format requires explicit mapping or fails closed",
|
|
)
|
|
|
|
|
|
def gemini_default_status(field: SourceField, profile: list[CoverageStatus]) -> CoverageStatus:
|
|
if profile:
|
|
cross_format = most_common(status.cross_format for status in profile) or "lossy-blocked"
|
|
return CoverageStatus(
|
|
surface=most_common(status.surface for status in profile)
|
|
or "gemini:generate_content standard",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip=most_common(status.canonical_roundtrip for status in profile)
|
|
or "extension-preserved",
|
|
cross_format=cross_format,
|
|
notes=next(
|
|
(status.notes for status in profile if status.cross_format == cross_format),
|
|
"Gemini field follows schema-level handling",
|
|
),
|
|
)
|
|
return CoverageStatus(
|
|
surface="gemini:generate_content standard",
|
|
same_format_runtime="native",
|
|
canonical_roundtrip="extension-preserved",
|
|
cross_format="lossy-blocked",
|
|
notes="Gemini documented field is preserved same-format; cross-format requires explicit mapping or fails closed",
|
|
)
|
|
|
|
|
|
def default_status(field: SourceField, profile: list[CoverageStatus]) -> CoverageStatus:
|
|
if field.provider == "OpenAI":
|
|
return openai_default_status(field, profile)
|
|
if field.provider == "Claude":
|
|
return claude_default_status(field)
|
|
if field.provider == "Gemini":
|
|
return gemini_default_status(field, profile)
|
|
raise ValueError(f"unsupported provider: {field.provider}")
|
|
|
|
|
|
def render_matrix(
|
|
fields: list[SourceField],
|
|
existing: dict[tuple[str, str, str], CoverageStatus],
|
|
profiles: dict[tuple[str, str], list[CoverageStatus]],
|
|
) -> str:
|
|
rows: list[str] = [
|
|
"# Format Field Coverage Matrix",
|
|
"",
|
|
"Last generated: 2026-06-03",
|
|
"",
|
|
"This file is generated from the schema inventory in `docs/api/provider-interface-definitions.md` and gives every documented schema field an explicit handling status. “处理到” here means the field is either mapped, preserved in same-format paths, rejected with a structured fail-closed error, or explicitly outside the current conversion surface. It does not mean every field can be cross-format converted.",
|
|
"",
|
|
"Provider schema updates do not require immediate conversion-code changes for runtime safety. Same-format runtime paths bypass canonical conversion, and same-format canonical roundtrip preserves provider extension fields. Cross-format conversion only enables fields with an audited semantic mapping; newly discovered or unknown provider fields default to structured fail-closed behavior until mapped.",
|
|
"",
|
|
"Regenerate with: `python3 docs/api/generate_format_field_coverage.py`.",
|
|
"",
|
|
"Statuses used in this matrix: `native`, `mapped`, `mapped/lossy-blocked`, `extension-preserved`, `unaudited`, `unsupported`, `invalid-enum`, `lossy-blocked`, `not-in-conversion-surface`.",
|
|
"",
|
|
"| Provider | Schema | Field | Required | Type | Surface | Same-Format Runtime | Canonical Roundtrip | Cross-Format | Notes |",
|
|
"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
|
|
]
|
|
|
|
for field in fields:
|
|
status = existing.get(
|
|
(field.provider, field.schema, field.field),
|
|
default_status(field, profiles[(field.provider, field.schema)]),
|
|
)
|
|
rows.append(
|
|
"| "
|
|
+ " | ".join(
|
|
[
|
|
field.provider,
|
|
f"`{escape_markdown_cell(field.schema)}`",
|
|
f"`{escape_markdown_cell(field.field)}`",
|
|
field.required,
|
|
f"`{escape_markdown_cell(field.field_type)}`",
|
|
escape_markdown_cell(status.surface),
|
|
escape_markdown_cell(status.same_format_runtime),
|
|
escape_markdown_cell(status.canonical_roundtrip),
|
|
escape_markdown_cell(status.cross_format),
|
|
escape_markdown_cell(status.notes),
|
|
]
|
|
)
|
|
+ " |"
|
|
)
|
|
|
|
rows.extend(["", f"Total covered schema fields: {len(fields)}."])
|
|
return "\n".join(rows) + "\n"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--definitions", type=Path, default=DEFAULT_DEFINITIONS)
|
|
parser.add_argument("--matrix", type=Path, default=DEFAULT_MATRIX)
|
|
parser.add_argument("--check", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
definitions = args.definitions.read_text()
|
|
current_matrix = args.matrix.read_text() if args.matrix.exists() else ""
|
|
fields = parse_provider_definition_fields(definitions)
|
|
existing, profiles = parse_existing_coverage(current_matrix)
|
|
next_matrix = render_matrix(fields, existing, profiles)
|
|
|
|
if args.check:
|
|
if current_matrix != next_matrix:
|
|
print(
|
|
f"{args.matrix} is not up to date; run "
|
|
"`python3 docs/api/generate_format_field_coverage.py`",
|
|
)
|
|
return 1
|
|
return 0
|
|
|
|
args.matrix.write_text(next_matrix)
|
|
print(f"wrote {len(fields)} field coverage rows to {args.matrix}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|