@@ -172,16 +172,23 @@ struct MLXServer: AsyncParsableCommand {
172172 } else {
173173 // Non-streaming: collect all chunks
174174 var fullText = " "
175+ var completionTokenCount = 0
175176 for await generation in stream {
176177 switch generation {
177178 case . chunk( let text) :
178179 fullText += text
180+ completionTokenCount += 1
179181 case . info, . toolCall:
180182 break
181183 }
182184 }
183185 await semaphore. signal ( )
184186
187+ // Approximate prompt tokens (chars / 4 is a reasonable heuristic for most tokenizers)
188+ let promptText = chatReq. messages. map { $0. content } . joined ( separator: " " )
189+ let estimatedPromptTokens = max ( 1 , promptText. count / 4 )
190+ let totalTokens = estimatedPromptTokens + completionTokenCount
191+
185192 let resp = ChatCompletionResponse (
186193 id: " chatcmpl- \( UUID ( ) . uuidString) " ,
187194 model: modelId,
@@ -193,7 +200,7 @@ struct MLXServer: AsyncParsableCommand {
193200 finishReason: " stop "
194201 )
195202 ] ,
196- usage: TokenUsage ( promptTokens: 0 , completionTokens: 0 , totalTokens: 0 )
203+ usage: TokenUsage ( promptTokens: estimatedPromptTokens , completionTokens: completionTokenCount , totalTokens: totalTokens )
197204 )
198205 let encoded = try JSONEncoder ( ) . encode ( resp)
199206 return Response (
@@ -211,6 +218,25 @@ struct MLXServer: AsyncParsableCommand {
211218 )
212219
213220 print ( " [mlx-server] ✅ Ready. Listening on http:// \( host) : \( port) " )
221+
222+ // ── Emit machine-readable ready event for Aegis integration ──
223+ let readyEvent : [ String : Any ] = [
224+ " event " : " ready " ,
225+ " port " : port,
226+ " model " : modelId,
227+ " engine " : " mlx " ,
228+ " vision " : false
229+ ]
230+ if let data = try ? JSONSerialization . data ( withJSONObject: readyEvent) ,
231+ let json = String ( data: data, encoding: . utf8) {
232+ print ( json)
233+ fflush ( stdout)
234+ }
235+
236+ // ── Handle SIGTERM/SIGINT for graceful shutdown ──
237+ // Note: C signal handlers can't safely do complex I/O.
238+ // Aegis detects process exit and fires onEngineStopped() automatically.
239+
214240 try await app. runService ( )
215241 }
216242}
0 commit comments