Frame-aware AI fallback module scaffolded under src/phase_z2_ai_fallback/ with master flag ai_fallback_enabled=False; normal-path AI call count remains 0. AI output constrained to builder_options_patch / partial_overrides / slot_mapping_proposal; MDX / frame_id / raw HTML / raw CSS mutations rejected at schema layer. IMP-46 cache gate (cache.py) raises AiFallbackCacheGateError unless visual_check_passed AND user_approved. Step 12 wires AI repair after IMP-30 provisional payload only; Step 17 stays blocked behind IMP-34 / IMP-35 prerequisites. AST isolation guard forbids fallback package from importing Phase Q / Kei / pipeline runtime symbols. Docs IMP-17 / IMP-31 bound to runtime module surface via 11-row structural test pin (test_docs_sync.py) so drift fails CI. Tests: 116 fallback / 161 phase_z2 regression / 526 scoped full sweep all passing. Existing pre-IMP-33 fixture issue in scripts/test_phase_t_* remains untouched (out of scope). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
"""IMP-33 u3 — AI fallback prompt builder (fallback path only).
|
|
|
|
System+user prompt for the Anthropic client (u4). MDX is READ-ONLY
|
|
(`feedback_ai_isolation_contract`); output is constrained to the u2
|
|
schema; frame_id swap is forbidden (V4 rank-1 protected,
|
|
`feedback_phase_z_spacing_direction`). Inputs per Stage 2 plan: V4
|
|
result (route=ai_adaptation_required, cardinality), frame_contract,
|
|
frame_visual HTML, figma_to_html_agent partial JSON, Internal Region,
|
|
MDX text.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from src.phase_z2_ai_fallback.schema import FORBIDDEN_KINDS, ProposalKind
|
|
|
|
V4_ROUTE_AI_ADAPTATION = "ai_adaptation_required"
|
|
|
|
_ALLOWED_KINDS = ", ".join(sorted(k.value for k in ProposalKind))
|
|
_FORBIDDEN_KINDS = ", ".join(sorted(FORBIDDEN_KINDS))
|
|
|
|
SYSTEM_PROMPT = (
|
|
"You are an IMP-33 AI fallback adapter for Phase Z slide composition.\n"
|
|
"STRICT RULES:\n"
|
|
" 1. MDX text in the user payload is READ-ONLY. Do NOT rewrite, "
|
|
"compress, or paraphrase MDX.\n"
|
|
" 2. Output MUST be a single JSON object conforming to AiFallbackProposal.\n"
|
|
f" 3. proposal_kind MUST be one of: {_ALLOWED_KINDS}.\n"
|
|
f" 4. Do NOT propose any of: {_FORBIDDEN_KINDS}.\n"
|
|
" 5. Do NOT change frame_id — V4 rank-1 frame is locked.\n"
|
|
" 6. Keep declared frame slots (text/table/image/details) populated.\n"
|
|
" 7. Respect Internal Region containment; place content units within "
|
|
"the declared region only."
|
|
)
|
|
|
|
|
|
def build_ai_fallback_prompt(
|
|
*,
|
|
v4_result: dict[str, Any],
|
|
frame_contract: dict[str, Any],
|
|
frame_visual_html: str,
|
|
figma_partial_json: dict[str, Any],
|
|
internal_region: dict[str, Any],
|
|
mdx_text: str,
|
|
) -> dict[str, str]:
|
|
"""Build system+user prompt strings for the fallback AI adapter.
|
|
|
|
Raises:
|
|
ValueError: when ``v4_result.route`` is not
|
|
``ai_adaptation_required`` — the fallback prompt MUST NOT be
|
|
built outside this route (normal-path AI call count must
|
|
remain 0; PZ-1).
|
|
"""
|
|
route = v4_result.get("route") or v4_result.get("imp05_route_hint")
|
|
if route != V4_ROUTE_AI_ADAPTATION:
|
|
raise ValueError(
|
|
f"build_ai_fallback_prompt: v4_result.route={route!r} is not "
|
|
f"{V4_ROUTE_AI_ADAPTATION!r}; fallback prompt MUST NOT be built "
|
|
"outside the AI adaptation route."
|
|
)
|
|
user_payload = {
|
|
"v4": {
|
|
"route": route,
|
|
"cardinality": v4_result.get("cardinality")
|
|
or v4_result.get("cardinality_signature"),
|
|
"label": v4_result.get("label"),
|
|
"frame_id": v4_result.get("frame_id"),
|
|
"rank": v4_result.get("rank"),
|
|
},
|
|
"frame_contract": frame_contract,
|
|
"frame_visual_html": frame_visual_html,
|
|
"figma_partial_json": figma_partial_json,
|
|
"internal_region": internal_region,
|
|
"mdx_text_READ_ONLY": mdx_text,
|
|
}
|
|
return {
|
|
"system": SYSTEM_PROMPT,
|
|
"user": json.dumps(user_payload, ensure_ascii=False),
|
|
}
|