Files
commander-ai-lab/coach/schemas/substitution_schema.py
T
KoalaTrapLord a5fe8cb3fa v23: React + TypeScript SPA migration
Complete frontend rewrite from vanilla HTML/CSS/JS to React 19 + TypeScript + Tailwind CSS v4.

Frontend (frontend/commander-ai-lab-ui/):
- Vite + React 19 + TypeScript + Tailwind CSS v4 + React Router
- 7 pages: BatchSim, Collection, Decks, AutoGen, Simulator, Coach, Training
- Reusable components: Spinner, StatusBadge, CardImage, ManaSymbols, EmptyState
- Full typed API client layer (8 modules matching all backend endpoints)
- Recharts for mana curves, win rate charts, progress bars
- Lucide icons throughout
- Custom dark theme with MTG mana color tokens
- Code splitting (recharts + react in separate chunks)

Backend (lab_api.py):
- SPA-aware static file serving with catch-all for client-side routing
- Fallback to legacy ui/ folder if React dist not built
- Updated startup log with SPA route list

Build system:
- .gitignore added (was missing - removes pyc, db, node_modules, etc.)
- build-full-installer-v2.py: v23 with Node.js check + React build step
- Vite build config with manual chunks for optimal loading

Also includes: V3 deck generator fixes, coach improvements, DeepSeek sim modules
2026-03-12 01:12:02 +00:00

93 lines
4.0 KiB
Python

"""
Commander AI Lab — Substitution Schemas
════════════════════════════════════════
Pydantic models and JSON Schema dicts for the
Smart Substitution Engine output.
"""
from typing import List, Optional
from pydantic import BaseModel, Field
# ══════════════════════════════════════════════════════════════
# Pydantic Models
# ══════════════════════════════════════════════════════════════
class CardAlternative(BaseModel):
"""A single substitute card suggestion."""
name: str = Field(description="Exact card name")
similarity_score: float = Field(default=0.0, description="Cosine similarity score (0-1)")
reason: str = Field(default="", description="Why this card is a good substitute")
source: str = Field(default="embedding", description="How it was found: 'embedding' or 'perplexity'")
role_overlap: List[str] = Field(default_factory=list, description="Shared functional roles")
cmc_delta: float = Field(default=0.0, description="CMC difference from original")
class DeckCardWithStatus(BaseModel):
"""A deck card enriched with ownership and substitution data."""
name: str
count: int = 1
category: str = ""
role_tags: List[str] = Field(default_factory=list)
reason: str = ""
estimated_price_usd: float = 0.0
synergy_with: List[str] = Field(default_factory=list)
# Ownership & substitution
owned: bool = True
owned_qty: int = 0
scryfall_id: str = ""
status: str = Field(default="owned", description="owned | substituted | missing")
original_card: Optional[str] = Field(default=None, description="Original card name if this is a substitution")
alternatives: List[CardAlternative] = Field(default_factory=list)
selected_substitute: Optional[str] = Field(default=None, description="User-selected substitute name")
class SubstitutionRequest(BaseModel):
"""Request for substitution on a single card."""
card_name: str
role_tags: List[str] = Field(default_factory=list)
category: str = ""
cmc: float = 0.0
color_identity: List[str] = Field(default_factory=list)
class SubstitutionBatchResult(BaseModel):
"""Result of a batch substitution pass."""
total_cards: int = 0
owned_count: int = 0
substituted_count: int = 0
missing_count: int = 0
cards: List[DeckCardWithStatus] = Field(default_factory=list)
# ══════════════════════════════════════════════════════════════
# JSON Schema dicts (for Perplexity fallback substitution)
# ══════════════════════════════════════════════════════════════
SUBSTITUTION_RESPONSE_SCHEMA = {
"type": "object",
"properties": {
"substitutions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"original": {"type": "string", "description": "Card that needs replacing"},
"substitute": {"type": "string", "description": "Recommended replacement from the allowed list"},
"reason": {"type": "string", "description": "Why this substitute works"},
"role_overlap": {
"type": "array",
"items": {"type": "string"},
"description": "Shared functional roles",
},
},
"required": ["original", "substitute", "reason"],
"additionalProperties": False,
},
},
},
"required": ["substitutions"],
"additionalProperties": False,
}