@@ -37,7 +37,11 @@ def __init__(self, session_id: str, results_dir: str = "./results"):
3737 self ._initialize_csv ()
3838
3939 def _initialize_csv (self ) -> None :
40- if not self .csv_path .exists ():
40+ # Write the header when the file is missing OR exists but is empty.
41+ # A leftover 0-byte file (e.g. from an interrupted/restarted run)
42+ # would otherwise be appended to without ever getting a header,
43+ # producing a headerless CSV that breaks report generation.
44+ if not self .csv_path .exists () or self .csv_path .stat ().st_size == 0 :
4145 with open (self .csv_path , "w" , newline = "" ) as f :
4246 csv .writer (f ).writerow (CSV_HEADER )
4347
@@ -88,37 +92,51 @@ def _f(v: str | None) -> float | None:
8892 return None
8993 return float (v )
9094
91- with open (csv_path , "r" ) as f :
92- for row in csv .DictReader (f ):
93- timestamp = datetime .fromisoformat (row ["timestamp" ])
94- if start_time is None or timestamp < start_time :
95- start_time = timestamp
96- if end_time is None or timestamp > end_time :
97- end_time = timestamp
98-
99- services .add (row ["service_name" ])
100- models .add (row .get ("model_name" , "" ))
101- workloads .add (row ["workload_type" ])
102- iterations_per_workload = max (iterations_per_workload , int (row ["iteration" ]) + 1 )
103-
104- metrics .append (
105- BenchmarkMetric (
106- session_id = row ["session_id" ],
107- service_name = row ["service_name" ],
108- model_name = row .get ("model_name" , "" ),
109- workload_type = row ["workload_type" ],
110- iteration = int (row ["iteration" ]),
111- ttft_ms = _f (row .get ("ttft_ms" )),
112- end_to_end_latency_ms = float (row ["end_to_end_latency_ms" ]),
113- tokens_generated = int (row ["tokens_generated" ]),
114- prompt_tokens = int (row ["prompt_tokens" ]),
115- tokens_per_sec = _f (row .get ("tokens_per_sec" )),
116- inter_token_latency_ms = _f (row .get ("inter_token_latency_ms" )),
117- attempts = int (row .get ("attempts" ) or 1 ),
118- timestamp = timestamp ,
119- error = row ["error" ] if row ["error" ] else None ,
120- )
95+ with open (csv_path , "r" , newline = "" ) as f :
96+ data_rows = [r for r in csv .reader (f ) if r ]
97+
98+ # Tolerate raw CSVs written without a header row. A genuine header
99+ # begins with the literal "session_id" column name; anything else is
100+ # treated as data and mapped positionally against CSV_HEADER. This
101+ # lets us still produce reports from files an interrupted/restarted
102+ # run left headerless.
103+ if data_rows and data_rows [0 ][0 ] == CSV_HEADER [0 ]:
104+ data_rows = data_rows [1 :]
105+
106+ for raw in data_rows :
107+ # Pad short rows so every column is present (mirrors DictReader's
108+ # restval behaviour) before zipping into a name->value mapping.
109+ padded = list (raw ) + ["" ] * (len (CSV_HEADER ) - len (raw ))
110+ row = dict (zip (CSV_HEADER , padded ))
111+ timestamp = datetime .fromisoformat (row ["timestamp" ])
112+ if start_time is None or timestamp < start_time :
113+ start_time = timestamp
114+ if end_time is None or timestamp > end_time :
115+ end_time = timestamp
116+
117+ services .add (row ["service_name" ])
118+ models .add (row .get ("model_name" , "" ))
119+ workloads .add (row ["workload_type" ])
120+ iterations_per_workload = max (iterations_per_workload , int (row ["iteration" ]) + 1 )
121+
122+ metrics .append (
123+ BenchmarkMetric (
124+ session_id = row ["session_id" ],
125+ service_name = row ["service_name" ],
126+ model_name = row .get ("model_name" , "" ),
127+ workload_type = row ["workload_type" ],
128+ iteration = int (row ["iteration" ]),
129+ ttft_ms = _f (row .get ("ttft_ms" )),
130+ end_to_end_latency_ms = float (row ["end_to_end_latency_ms" ]),
131+ tokens_generated = int (row ["tokens_generated" ]),
132+ prompt_tokens = int (row ["prompt_tokens" ]),
133+ tokens_per_sec = _f (row .get ("tokens_per_sec" )),
134+ inter_token_latency_ms = _f (row .get ("inter_token_latency_ms" )),
135+ attempts = int (row .get ("attempts" ) or 1 ),
136+ timestamp = timestamp ,
137+ error = row ["error" ] if row ["error" ] else None ,
121138 )
139+ )
122140
123141 return BenchmarkSession (
124142 session_id = session_id ,
0 commit comments