@@ -887,6 +887,11 @@ func handleChatCompletion(
887887 let promptTokenCount = lmInput. text. tokens. size
888888 let systemHash = systemPromptText. hashValue
889889
890+ // llama-server style: announce prefill start
891+ print ( " srv slot_launch: id 0 | prompt= \( promptTokenCount) t | prefilling... " )
892+ fflush ( stdout)
893+ let prefillStart = Date ( )
894+
890895 // ── Cache-aware generation ──
891896 let stream : AsyncStream < Generation > = try await container. perform { context in
892897 let cache = context. model. newCache ( parameters: params)
@@ -940,12 +945,14 @@ func handleChatCompletion(
940945 return handleChatStreaming (
941946 stream: stream, modelId: modelId, stopSequences: stopSequences,
942947 includeUsage: includeUsage, promptTokenCount: promptTokenCount,
943- jsonMode: jsonMode, semaphore: semaphore, stats: stats, genStart: genStart
948+ jsonMode: jsonMode, semaphore: semaphore, stats: stats,
949+ genStart: genStart, prefillStart: prefillStart
944950 )
945951 } else {
946952 return try await handleChatNonStreaming (
947953 stream: stream, modelId: modelId, stopSequences: stopSequences,
948- promptTokenCount: promptTokenCount, jsonMode: jsonMode, semaphore: semaphore, stats: stats, genStart: genStart
954+ promptTokenCount: promptTokenCount, jsonMode: jsonMode, semaphore: semaphore,
955+ stats: stats, genStart: genStart, prefillStart: prefillStart
949956 )
950957 }
951958}
@@ -961,7 +968,8 @@ func handleChatStreaming(
961968 jsonMode: Bool = false ,
962969 semaphore: AsyncSemaphore,
963970 stats: ServerStats,
964- genStart: Date
971+ genStart: Date,
972+ prefillStart: Date
965973) - > Response {
966974 let ( sseStream, cont) = AsyncStream< String> . makeStream( )
967975 Task {
@@ -981,9 +989,12 @@ func handleChatStreaming(
981989 if completionTokenCount % 8 == 0 {
982990 try ? await Task . sleep ( for: . microseconds( 50 ) )
983991 }
984- // Real-time stdout: print token text as it arrives (no newline)
992+ // Real-time stdout: on first token, log prefill completion + start generate line
985993 if firstToken {
986- print ( " srv generate: prompt= \( promptTokenCount) t | " , terminator: " " )
994+ let prefillDur = Date ( ) . timeIntervalSince ( prefillStart)
995+ let prefillTokPerSec = prefillDur > 0 ? Double ( promptTokenCount) / prefillDur : 0
996+ print ( " srv slot update: id 0 | prefill done | n_tokens= \( promptTokenCount) , t= \( String ( format: " %.2f " , prefillDur) ) s, \( String ( format: " %.1f " , prefillTokPerSec) ) t/s " )
997+ print ( " srv generate: id 0 | " , terminator: " " )
987998 firstToken = false
988999 }
9891000 print ( text, terminator: " " )
@@ -999,7 +1010,7 @@ func handleChatStreaming(
9991010 if includeUsage {
10001011 cont. yield ( sseUsageChunk ( modelId: modelId, promptTokens: promptTokenCount, completionTokens: completionTokenCount) )
10011012 }
1002- cont. yield ( " data: [DONE] \n \n " )
1013+ cont. yield ( " data: [DONE] \r \n \r \n " )
10031014 cont. finish ( )
10041015 stopped = true
10051016 } else {
@@ -1023,7 +1034,7 @@ func handleChatStreaming(
10231034 if includeUsage {
10241035 cont. yield ( sseUsageChunk ( modelId: modelId, promptTokens: promptTokenCount, completionTokens: completionTokenCount) )
10251036 }
1026- cont. yield ( " data: [DONE] \n \n " )
1037+ cont. yield ( " data: [DONE] \r \n \r \n " )
10271038 cont. finish ( )
10281039 // llama-server style: print newline then full response JSON
10291040 print ( " " ) // end the real-time token stream line
@@ -1072,7 +1083,8 @@ func handleChatNonStreaming(
10721083 jsonMode: Bool = false ,
10731084 semaphore: AsyncSemaphore,
10741085 stats: ServerStats,
1075- genStart: Date
1086+ genStart: Date,
1087+ prefillStart: Date
10761088) async throws -> Response {
10771089 var fullText = " "
10781090 var completionTokenCount = 0
@@ -1089,9 +1101,12 @@ func handleChatNonStreaming(
10891101 if completionTokenCount % 8 == 0 {
10901102 try ? await Task . sleep ( for: . microseconds( 50 ) )
10911103 }
1092- // Real-time stdout: print token text as it arrives (no newline)
1104+ // Real-time stdout: on first token, log prefill completion + start generate line
10931105 if firstToken {
1094- print ( " srv generate: prompt= \( promptTokenCount) t | " , terminator: " " )
1106+ let prefillDur = Date ( ) . timeIntervalSince ( prefillStart)
1107+ let prefillTokPerSec = prefillDur > 0 ? Double ( promptTokenCount) / prefillDur : 0
1108+ print ( " srv slot update: id 0 | prefill done | n_tokens= \( promptTokenCount) , t= \( String ( format: " %.2f " , prefillDur) ) s, \( String ( format: " %.1f " , prefillTokPerSec) ) t/s " )
1109+ print ( " srv generate: id 0 | " , terminator: " " )
10951110 firstToken = false
10961111 }
10971112 print ( text, terminator: " " )
@@ -1497,7 +1512,7 @@ func sseChunk(modelId: String, delta: String, finishReason: String?) -> String {
14971512 " choices " : [ choiceObj]
14981513 ]
14991514 let data = try ! JSONSerialization . data ( withJSONObject: chunk)
1500- return " data: \( String ( data: data, encoding: . utf8) !) \n \n "
1515+ return " data: \( String ( data: data, encoding: . utf8) !) \r \n \r \n "
15011516}
15021517
15031518func sseUsageChunk( modelId: String, promptTokens: Int, completionTokens: Int) - > String {
@@ -1514,7 +1529,7 @@ func sseUsageChunk(modelId: String, promptTokens: Int, completionTokens: Int) ->
15141529 ]
15151530 ]
15161531 let data = try ! JSONSerialization . data ( withJSONObject: chunk)
1517- return " data: \( String ( data: data, encoding: . utf8) !) \n \n "
1532+ return " data: \( String ( data: data, encoding: . utf8) !) \r \n \r \n "
15181533}
15191534
15201535func sseToolCallChunk( modelId: String, index: Int, name: String, arguments: String) - > String {
@@ -1540,7 +1555,7 @@ func sseToolCallChunk(modelId: String, index: Int, name: String, arguments: Stri
15401555 ] as [ String : Any ] ]
15411556 ]
15421557 let data = try ! JSONSerialization . data ( withJSONObject: chunk)
1543- return " data: \( String ( data: data, encoding: . utf8) !) \n \n "
1558+ return " data: \( String ( data: data, encoding: . utf8) !) \r \n \r \n "
15441559}
15451560
15461561func sseTextChunk( modelId: String, text: String, finishReason: String? ) - > String {
@@ -1559,7 +1574,7 @@ func sseTextChunk(modelId: String, text: String, finishReason: String?) -> Strin
15591574 " choices " : [ choiceObj]
15601575 ]
15611576 let data = try ! JSONSerialization . data ( withJSONObject: chunk)
1562- return " data: \( String ( data: data, encoding: . utf8) !) \n \n "
1577+ return " data: \( String ( data: data, encoding: . utf8) !) \r \n \r \n "
15631578}
15641579
15651580func serializeToolCallArgs( _ args: [ String: JSONValue] ) - > String {
0 commit comments