gmal-scope-builder/backend/app/schemas/project.py
DJP bc778ce7af P2: Iterative prompting + RFP brief analysis engine
Iterative Prompting:
- Chat box on Match Review tab for natural language refinement
- "re-run under 70%" / "ignore zero volume" / "set all volumes to 1"
- Claude interprets instruction into structured actions
- Actions: rematch_below_threshold, rematch_specific, delete_assets, set_volume
- Re-matches affected assets automatically after refinement
- Chat log shows instruction history

RFP/Brief Analysis:
- New "Brief Analysis" tab between Upload and Match Review
- Extracts: summary, objectives, KPIs, channels, audiences, deliverable categories,
  constraints, timeline, budget, complexity assessment
- Generates prioritized discovery questions (Red/Amber/Green)
- Questions include category, rationale, and priority level
- Stored as JSON in project.brief_analysis field
- Uploaded files now saved to data dir for re-analysis
- Re-analyze button to refresh analysis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:15:31 -04:00

115 lines
2.4 KiB
Python

from pydantic import BaseModel
from datetime import datetime
class ProjectCreate(BaseModel):
name: str
client_name: str | None = None
description: str | None = None
model_type: str = "current_oplus"
class ProjectUpdate(BaseModel):
name: str | None = None
client_name: str | None = None
description: str | None = None
model_type: str | None = None
class ProjectOut(BaseModel):
id: int
name: str
client_name: str | None
description: str | None
model_type: str
status: str
source_filename: str | None
parse_stage: str | None = None
has_brief_analysis: bool = False
ai_input_tokens: int = 0
ai_output_tokens: int = 0
ai_cost_usd: float = 0
ai_call_count: int = 0
created_at: datetime
updated_at: datetime
asset_count: int = 0
class Config:
from_attributes = True
class ClientAssetOut(BaseModel):
id: int
project_id: int
raw_name: str | None
raw_description: str | None
volume: int
sort_order: int | None
class Config:
from_attributes = True
class ClientAssetUpdate(BaseModel):
raw_name: str | None = None
raw_description: str | None = None
volume: int | None = None
class MatchOut(BaseModel):
id: int
client_asset_id: int
gmal_asset_id: int
gmal_id: str | None = None
gmal_name: str | None = None
gmal_unique_name: str | None = None
confidence: str
confidence_score: float | None
ai_reasoning: str | None
caveat_text: str | None
is_selected: bool
rank: int
class Config:
from_attributes = True
class MatchSelectRequest(BaseModel):
is_selected: bool = True
class ManualMatchRequest(BaseModel):
gmal_asset_id: int
class RatecardLineOut(BaseModel):
id: int
client_asset_id: int
client_asset_name: str | None = None
gmal_asset_id: int
gmal_id: str | None = None
role_id: int
role_title: str | None = None
discipline: str | None = None
base_hours: float | None
volume: int
total_hours: float | None
manual_override: float | None
notes: str | None
class Config:
from_attributes = True
class RatecardLineUpdate(BaseModel):
manual_override: float | None = None
notes: str | None = None
class RatecardSummary(BaseModel):
project_id: int
project_name: str
model_type: str
total_assets: int
total_hours: float
lines: list[RatecardLineOut]