2025-10-18 22:36:20 -05:00
"""
Prompt template for strategy brainstorming .
"""
from typing import List
from models . input_models import EnrichedTelemetryWebhook , RaceContext
from utils . validators import TelemetryAnalyzer
from config import get_settings
def build_brainstorm_prompt_fast (
enriched_telemetry : List [ EnrichedTelemetryWebhook ] ,
2025-10-19 05:29:30 -05:00
race_context : RaceContext ,
strategy_history : List [ dict ] = None
2025-10-18 22:36:20 -05:00
) - > str :
2025-10-19 03:57:03 -05:00
""" Build a faster, more concise prompt for quicker responses (lap-level data). """
2025-10-18 23:56:53 -05:00
settings = get_settings ( )
count = settings . strategy_count
2025-10-18 22:36:20 -05:00
latest = max ( enriched_telemetry , key = lambda x : x . lap )
2025-10-19 03:57:03 -05:00
pit_window = latest . optimal_pit_window
2025-10-18 22:36:20 -05:00
2025-10-19 04:28:49 -05:00
# Format position and competitive info
position = race_context . driver_state . current_position
gap_to_leader = race_context . driver_state . gap_to_leader
gap_to_ahead = race_context . driver_state . gap_to_ahead
comp_info = f " P { position } "
if gap_to_ahead > 0 :
comp_info + = f " , { gap_to_ahead : .1f } s behind P { position - 1 } "
if gap_to_leader > 0 and position > 1 :
comp_info + = f " , { gap_to_leader : .1f } s from leader "
2025-10-19 05:29:30 -05:00
# Format strategy history (last 3 strategies)
history_text = " "
if strategy_history and len ( strategy_history ) > 0 :
recent_history = strategy_history [ - 3 : ] # Last 3 strategies
history_lines = [ ]
for h in recent_history :
history_lines . append ( f " Lap { h [ ' lap ' ] } : { h [ ' strategy_name ' ] } (Risk: { h [ ' risk_level ' ] } ) " )
history_text = f " \n \n PAST STRATEGIES: \n " + " \n " . join ( history_lines )
history_text + = f " \n \n REQUIREMENT: If changing from previous strategy ' { recent_history [ - 1 ] [ ' strategy_name ' ] } ' , explain WHY the switch is necessary. If staying with same approach, explain continuity. "
else :
history_text = " \n \n NOTE: This is the first strategy generation - no previous strategy to compare. "
2025-10-18 23:56:53 -05:00
if count == 1 :
# Ultra-fast mode: just generate 1 strategy
return f """ Generate 1 F1 race strategy for { race_context . driver_state . driver_name } at { race_context . race_info . track_name } .
2025-10-18 22:36:20 -05:00
2025-10-19 04:28:49 -05:00
CURRENT : Lap { race_context . race_info . current_lap } / { race_context . race_info . total_laps } , { comp_info } , { race_context . driver_state . current_tire_compound } tires ( { race_context . driver_state . tire_age_laps } laps old )
2025-10-18 22:36:20 -05:00
2025-10-19 05:29:30 -05:00
TELEMETRY : Tire deg { latest . tire_degradation_rate : .2 f } , Cliff risk { latest . tire_cliff_risk : .2 f } , Pace { latest . pace_trend } , Position trend { latest . position_trend } , Competitive pressure { latest . competitive_pressure : .2 f } { history_text }
2025-10-18 23:56:53 -05:00
2025-10-19 04:28:49 -05:00
Generate 1 optimal strategy considering competitive position . Min 2 tire compounds required .
2025-10-18 23:56:53 -05:00
2025-10-19 05:29:30 -05:00
JSON : { { " strategies " : [ { { " strategy_id " : 1 , " strategy_name " : " name " , " stop_count " : 1 , " pit_laps " : [ 32 ] , " tire_sequence " : [ " medium " , " hard " ] , " brief_description " : " one sentence " , " reasoning " : " detailed explanation including strategy change rationale if applicable " , " risk_level " : " medium " , " key_assumption " : " main assumption " } } ] } } """
2025-10-18 23:56:53 -05:00
elif count < = 5 :
# Fast mode: 2-5 strategies with different approaches
return f """ Generate { count } diverse F1 race strategies for { race_context . driver_state . driver_name } at { race_context . race_info . track_name } .
2025-10-19 04:28:49 -05:00
CURRENT : Lap { race_context . race_info . current_lap } / { race_context . race_info . total_laps } , { comp_info } , { race_context . driver_state . current_tire_compound } tires ( { race_context . driver_state . tire_age_laps } laps old )
TELEMETRY : Tire deg { latest . tire_degradation_rate : .2 f } , Cliff risk { latest . tire_cliff_risk : .2 f } , Pace { latest . pace_trend } , Delta { latest . performance_delta : + .2 f } s , Position trend { latest . position_trend } , Competitive pressure { latest . competitive_pressure : .2 f }
2025-10-18 23:56:53 -05:00
2025-10-19 05:29:30 -05:00
COMPETITIVE SITUATION : Gap to ahead { gap_to_ahead : .1 f } s ( { " DRS RANGE - attack opportunity! " if gap_to_ahead < 1.0 else " close battle " if gap_to_ahead < 3.0 else " need to push " } ) { history_text }
2025-10-18 22:36:20 -05:00
2025-10-19 04:28:49 -05:00
Generate { count } strategies balancing tire management with competitive pressure . Consider if aggressive undercut makes sense given gaps . Min 2 tire compounds each .
2025-10-18 23:56:53 -05:00
2025-10-19 05:29:30 -05:00
CRITICAL : Each strategy MUST include ' reasoning ' field explaining the approach and , if applicable , why it differs from or continues the previous strategy .
JSON : { { " strategies " : [ { { " strategy_id " : 1 , " strategy_name " : " Conservative Stay Out " , " stop_count " : 1 , " pit_laps " : [ 35 ] , " tire_sequence " : [ " medium " , " hard " ] , " brief_description " : " extend current stint then hard tires to end " , " reasoning " : " Detailed explanation of this strategy including why we ' re switching from/continuing previous approach based on current telemetry and competitive situation " , " risk_level " : " low " , " key_assumption " : " tire cliff risk stays below 0.7 " } } ] } } """
2025-10-18 23:56:53 -05:00
return f """ Generate { count } F1 race strategies for { race_context . driver_state . driver_name } at { race_context . race_info . track_name } .
2025-10-19 04:28:49 -05:00
CURRENT : Lap { race_context . race_info . current_lap } / { race_context . race_info . total_laps } , { comp_info } , { race_context . driver_state . current_tire_compound } tires ( { race_context . driver_state . tire_age_laps } laps old )
TELEMETRY : Tire deg { latest . tire_degradation_rate : .2 f } , Cliff risk { latest . tire_cliff_risk : .2 f } , Pace { latest . pace_trend } , Delta { latest . performance_delta : + .2 f } s , Position trend { latest . position_trend } , Competitive pressure { latest . competitive_pressure : .2 f }
2025-10-18 23:56:53 -05:00
2025-10-19 05:29:30 -05:00
COMPETITIVE : Gap ahead { gap_to_ahead : .1 f } s , Position trending { latest . position_trend } { history_text }
2025-10-18 22:36:20 -05:00
2025-10-19 04:28:49 -05:00
Generate { count } diverse strategies considering both tire management AND competitive positioning . Min 2 compounds .
2025-10-18 22:36:20 -05:00
2025-10-19 05:29:30 -05:00
CRITICAL : Each strategy MUST include ' reasoning ' field with detailed rationale and strategy continuity / change explanation .
JSON : { { " strategies " : [ { { " strategy_id " : 1 , " strategy_name " : " name " , " stop_count " : 1 , " pit_laps " : [ 32 ] , " tire_sequence " : [ " medium " , " hard " ] , " brief_description " : " one sentence " , " reasoning " : " Comprehensive explanation including strategy evolution context " , " risk_level " : " low|medium|high|critical " , " key_assumption " : " main assumption " } } ] } } """
2025-10-18 22:36:20 -05:00
def build_brainstorm_prompt (
enriched_telemetry : List [ EnrichedTelemetryWebhook ] ,
2025-10-19 05:29:30 -05:00
race_context : RaceContext ,
strategy_history : List [ dict ] = None
2025-10-18 22:36:20 -05:00
) - > str :
"""
Build the brainstorm prompt for Gemini .
Args :
enriched_telemetry : Recent enriched telemetry data
race_context : Current race context
2025-10-19 05:29:30 -05:00
strategy_history : List of previous strategies for context
2025-10-18 22:36:20 -05:00
Returns :
Formatted prompt string
"""
2025-10-19 03:57:03 -05:00
# Get latest telemetry
latest = max ( enriched_telemetry , key = lambda x : x . lap )
2025-10-18 22:36:20 -05:00
2025-10-19 03:57:03 -05:00
# Format telemetry data (lap-level)
2025-10-18 22:36:20 -05:00
telemetry_data = [ ]
for t in sorted ( enriched_telemetry , key = lambda x : x . lap , reverse = True ) [ : 10 ] :
telemetry_data . append ( {
" lap " : t . lap ,
2025-10-19 03:57:03 -05:00
" tire_degradation_rate " : round ( t . tire_degradation_rate , 3 ) ,
" pace_trend " : t . pace_trend ,
" tire_cliff_risk " : round ( t . tire_cliff_risk , 3 ) ,
" optimal_pit_window " : t . optimal_pit_window ,
2025-10-19 04:28:49 -05:00
" performance_delta " : round ( t . performance_delta , 2 ) ,
" competitive_pressure " : round ( t . competitive_pressure , 3 ) ,
" position_trend " : t . position_trend
2025-10-18 22:36:20 -05:00
} )
# Format competitors
competitors_data = [ ]
for c in race_context . competitors :
competitors_data . append ( {
" position " : c . position ,
" driver " : c . driver ,
" tire_compound " : c . tire_compound ,
" tire_age_laps " : c . tire_age_laps ,
" gap_seconds " : round ( c . gap_seconds , 1 )
} )
2025-10-19 05:29:30 -05:00
# Format strategy history
history_section = " "
if strategy_history and len ( strategy_history ) > 0 :
history_section = " \n \n STRATEGY HISTORY (Recent decisions): \n "
for h in strategy_history [ - 5 : ] : # Last 5 strategies
history_section + = f " - Lap { h [ ' lap ' ] } : { h [ ' strategy_name ' ] } (Risk: { h [ ' risk_level ' ] } ) - { h . get ( ' brief_description ' , ' N/A ' ) } \n "
history_section + = f " \n CONTEXT: Previous strategy was ' { strategy_history [ - 1 ] [ ' strategy_name ' ] } ' . If recommending a change, clearly explain WHY. If continuing same approach, explain the continuity. \n "
2025-10-19 04:28:49 -05:00
prompt = f """ You are an expert F1 strategist. Generate 20 diverse race strategies based on lap-level telemetry AND competitive positioning.
2025-10-18 22:36:20 -05:00
2025-10-19 03:57:03 -05:00
LAP - LEVEL TELEMETRY METRICS :
- tire_degradation_rate : 0 - 1 ( higher = worse tire wear )
- tire_cliff_risk : 0 - 1 ( probability of hitting tire cliff )
- pace_trend : " improving " , " stable " , or " declining "
2025-10-19 04:28:49 -05:00
- position_trend : " gaining " , " stable " , or " losing " positions
- competitive_pressure : 0 - 1 ( combined metric from position and gaps )
2025-10-19 03:57:03 -05:00
- optimal_pit_window : [ start_lap , end_lap ] recommended pit range
- performance_delta : seconds vs baseline ( negative = slower )
2025-10-18 22:36:20 -05:00
RACE STATE :
Track : { race_context . race_info . track_name }
Current Lap : { race_context . race_info . current_lap } / { race_context . race_info . total_laps }
Weather : { race_context . race_info . weather_condition }
Track Temperature : { race_context . race_info . track_temp_celsius } ° C
DRIVER STATE :
Driver : { race_context . driver_state . driver_name }
Position : P { race_context . driver_state . current_position }
Current Tires : { race_context . driver_state . current_tire_compound } ( { race_context . driver_state . tire_age_laps } laps old )
Fuel Remaining : { race_context . driver_state . fuel_remaining_percent } %
COMPETITORS :
{ competitors_data }
ENRICHED TELEMETRY ( Last { len ( telemetry_data ) } laps , newest first ) :
2025-10-19 05:29:30 -05:00
{ telemetry_data } { history_section }
2025-10-18 22:36:20 -05:00
KEY INSIGHTS :
2025-10-19 03:57:03 -05:00
- Latest tire degradation rate : { latest . tire_degradation_rate : .3 f }
- Latest tire cliff risk : { latest . tire_cliff_risk : .3 f }
- Latest pace trend : { latest . pace_trend }
- Optimal pit window : Laps { latest . optimal_pit_window [ 0 ] } - { latest . optimal_pit_window [ 1 ] }
2025-10-18 22:36:20 -05:00
- Laps remaining : { race_context . race_info . total_laps - race_context . race_info . current_lap }
TASK : Generate exactly 20 diverse strategies .
DIVERSITY : Conservative ( 1 - stop ) , Standard ( balanced ) , Aggressive ( undercut ) , Reactive ( competitor ) , Contingency ( safety car )
RULES :
- Pit laps : { race_context . race_info . current_lap + 1 } to { race_context . race_info . total_laps - 1 }
- Min 2 tire compounds ( F1 rule )
2025-10-19 03:57:03 -05:00
- Consider optimal pit window and tire cliff risk
2025-10-18 22:36:20 -05:00
For each strategy provide :
- strategy_id : 1 - 20
- strategy_name : Short descriptive name
- stop_count : 1 , 2 , or 3
- pit_laps : [ array of lap numbers ]
- tire_sequence : [ array of compounds : " soft " , " medium " , " hard " ]
- brief_description : One sentence rationale
2025-10-19 05:29:30 -05:00
- reasoning : DETAILED explanation ( 3 - 5 sentences ) including : ( 1 ) Why this strategy fits current conditions , ( 2 ) How it addresses telemetry trends , ( 3 ) Strategy evolution - why changing from or continuing previous approach
2025-10-18 22:36:20 -05:00
- risk_level : " low " , " medium " , " high " , or " critical "
- key_assumption : Main assumption this strategy relies on
2025-10-19 05:29:30 -05:00
CRITICAL : The ' reasoning ' field must include strategy continuity / change rationale relative to past decisions .
2025-10-18 22:36:20 -05:00
OUTPUT FORMAT ( JSON only , no markdown ) :
{ {
" strategies " : [
{ {
" strategy_id " : 1 ,
" strategy_name " : " Conservative 1-Stop " ,
" stop_count " : 1 ,
" pit_laps " : [ 32 ] ,
" tire_sequence " : [ " medium " , " hard " ] ,
" brief_description " : " Extend mediums to lap 32, safe finish on hards " ,
2025-10-19 05:29:30 -05:00
" reasoning " : " Current tire degradation at 0.45 suggests we can safely extend to lap 32 before hitting the cliff. This maintains our conservative approach from lap 3 as conditions haven ' t changed significantly - tire deg is stable and we ' re not under immediate competitive pressure. The hard tire finish provides low-risk race completion. " ,
2025-10-18 22:36:20 -05:00
" risk_level " : " low " ,
" key_assumption " : " Tire degradation stays below 0.85 until lap 32 "
} }
]
} } """
return prompt