@@ -90,11 +90,19 @@ std::string random_id() {
9090 return ss.str ();
9191}
9292
93- std::string usage_json (int prompt_tokens, int completion_tokens) {
93+ std::string usage_json (int prompt_tokens, int completion_tokens, double ttft_ms = -1.0 ,
94+ double generation_ms = -1.0 , double decode_tps = -1.0 ) {
9495 std::ostringstream o;
9596 const int total = prompt_tokens + completion_tokens;
9697 o << " \" usage\" :{\" prompt_tokens\" :" << prompt_tokens << " ,\" completion_tokens\" :" << completion_tokens
97- << " ,\" total_tokens\" :" << total << " }" ;
98+ << " ,\" total_tokens\" :" << total;
99+ if (ttft_ms >= 0.0 )
100+ o << " ,\" ttft_ms\" :" << std::fixed << std::setprecision (3 ) << ttft_ms;
101+ if (generation_ms >= 0.0 )
102+ o << " ,\" generation_ms\" :" << std::fixed << std::setprecision (3 ) << generation_ms;
103+ if (decode_tps >= 0.0 )
104+ o << " ,\" decode_tps\" :" << std::fixed << std::setprecision (2 ) << decode_tps;
105+ o << " }" ;
98106 return o.str ();
99107}
100108
@@ -313,13 +321,21 @@ int main(int argc, char** argv) {
313321 std::vector<int > stream_ids;
314322 stream_ids.reserve ((size_t )max_tokens);
315323 sparkinfer_server::ThinkingStreamSplitter splitter (enable_thinking);
324+ const auto wall_start = std::chrono::steady_clock::now ();
325+ std::chrono::steady_clock::time_point first_tok_time;
326+ bool saw_first_tok = false ;
316327 auto on_tok = [&](int tid) {
328+ if (!saw_first_tok) {
329+ first_tok_time = std::chrono::steady_clock::now ();
330+ saw_first_tok = true ;
331+ }
317332 std::string piece = g_tokenizer.decode_delta (stream_ids, tid);
318333 const auto delta = splitter.feed (piece);
319334 write_stream_delta (sink, cid, created, " reasoning_content" , delta.reasoning_content );
320335 write_stream_delta (sink, cid, created, " content" , delta.content );
321336 };
322337 engine.complete_streaming (prompt_ids, max_tokens, on_tok);
338+ const auto wall_end = std::chrono::steady_clock::now ();
323339 sparkinfer_server::ThinkingStreamSplitter::Delta flush;
324340 splitter.finish (flush);
325341 write_stream_delta (sink, cid, created, " reasoning_content" , flush.reasoning_content );
@@ -332,11 +348,27 @@ int main(int argc, char** argv) {
332348 }
333349 const int prompt_tokens = (int )prompt_ids.size ();
334350 const int completion_tokens = (int )stream_ids.size ();
351+ const auto & timing = engine.last_timing ();
352+ double ttft_ms = timing.ttft_ms ;
353+ double generation_ms = timing.generation_ms ;
354+ double decode_tps = timing.decode_tps ;
355+ if (generation_ms < 0.0 ) {
356+ generation_ms = std::chrono::duration<double , std::milli>(wall_end - wall_start).count ();
357+ }
358+ if (ttft_ms < 0.0 && saw_first_tok) {
359+ ttft_ms = std::chrono::duration<double , std::milli>(first_tok_time - wall_start).count ();
360+ }
361+ if (decode_tps < 0.0 && completion_tokens > 0 && generation_ms > 0.0 ) {
362+ const double decode_ms =
363+ (ttft_ms >= 0.0 ) ? std::max (generation_ms - ttft_ms, 1.0 ) : generation_ms;
364+ decode_tps = (double )completion_tokens * 1000.0 / decode_ms;
365+ }
335366 std::ostringstream usage_chunk;
336367 usage_chunk << " data: {\" id\" :\" " << cid << " \" ,\" object\" :\" chat.completion.chunk\" ,"
337368 << " \" created\" :" << created << " ,\" model\" :\" " << g_model_name << " \" ,"
338369 << " \" choices\" :[],"
339- << usage_json (prompt_tokens, completion_tokens) << " }\n\n " ;
370+ << usage_json (prompt_tokens, completion_tokens, ttft_ms, generation_ms, decode_tps)
371+ << " }\n\n " ;
340372 sink.write (usage_chunk.str ().c_str (), (size_t )usage_chunk.str ().size ());
341373 std::string tail =
342374 " data: {\" id\" :\" " + cid +
0 commit comments