@@ -50,6 +50,7 @@ final class PostProcessingPipeline {
5050 func process( rawText: String , engine: ( any ASREngine ) ? , completion: @escaping ( String ? ) -> Void ) {
5151 processingQueue. async { [ weak self] in
5252 guard let self = self else { return }
53+ let pipelineStart = ContinuousClock . now
5354
5455 guard !rawText. isEmpty else {
5556 logger. info ( " 最终识别结果: (无) " )
@@ -58,26 +59,48 @@ final class PostProcessingPipeline {
5859 }
5960
6061 // Stage 1: Term normalization (domain-specific)
62+ let termNormStart = ContinuousClock . now
6163 let normalizedText = self . termNormalizer? . normalize ( rawText) ?? rawText
62- if normalizedText != rawText {
64+ let termNormMs = AnalyticsService . elapsedMs ( since: termNormStart)
65+ let termNormChanged = normalizedText != rawText
66+ if termNormChanged {
6367 logger. info ( " TermNormalizer: \( rawText, privacy: . public) → \( normalizedText, privacy: . public) " )
6468 }
6569
6670 // Stage 2: ITN (inverse text normalization) for engines that need it
6771 var processedText = normalizedText
72+ let itnApplied : Bool
73+ let itnMs : Int
6874 if let engine = engine, engine. needsITN, let itn = self . itn {
75+ let itnStart = ContinuousClock . now
76+ let before = processedText
6977 processedText = itn. normalize ( text: processedText)
70- if processedText != normalizedText {
78+ itnMs = AnalyticsService . elapsedMs ( since: itnStart)
79+ itnApplied = processedText != before
80+ if itnApplied {
7181 logger. info ( " ITN: \( normalizedText, privacy: . public) → \( processedText, privacy: . public) " )
7282 }
83+ } else {
84+ itnApplied = false
85+ itnMs = 0
7386 }
7487
7588 // Branch: engines that don't need punctuation skip CSC + punctuation
7689 guard let engine = engine, engine. needsPunctuation else {
7790 Task { [ weak self] in
7891 guard let self else { return }
92+ let cloudRewriteStart = ContinuousClock . now
7993 let rewrittenText = await self . cloudRewriteService. rewriteOrPassthrough ( processedText)
94+ let cloudRewriteMs = AnalyticsService . elapsedMs ( since: cloudRewriteStart)
8095 self . processingQueue. async {
96+ Self . trackPostProcessingCompleted (
97+ pipelineStart: pipelineStart,
98+ termNormMs: termNormMs, termNormChanged: termNormChanged,
99+ itnMs: itnMs, itnApplied: itnApplied,
100+ cscMs: 0 , cscApplied: false ,
101+ punctMs: 0 , punctuationApplied: false ,
102+ cloudRewriteMs: cloudRewriteMs
103+ )
81104 logger. info ( " 最终结果: \( rewrittenText, privacy: . public) " )
82105 completion ( rewrittenText)
83106 }
@@ -86,26 +109,69 @@ final class PostProcessingPipeline {
86109 }
87110
88111 // Stage 3: CSC (Chinese spelling correction)
112+ let cscStart = ContinuousClock . now
89113 let correctedText = self . corrector? . correctSpelling ( processedText) ?? processedText
114+ let cscMs = AnalyticsService . elapsedMs ( since: cscStart)
115+ let cscApplied = correctedText != processedText
90116 logger. info ( " 原始文本: \( processedText, privacy: . public) " )
91- if correctedText != processedText {
117+ if cscApplied {
92118 logger. info ( " CSC 纠正: \( correctedText, privacy: . public) " )
93119 } else {
94120 logger. info ( " CSC 未修改文本 " )
95121 }
96122
97123 // Stage 4: Punctuation
124+ let punctStart = ContinuousClock . now
98125 let punctuatedText = self . punctuator? . addPunctuation ( text: correctedText) ?? correctedText
126+ let punctMs = AnalyticsService . elapsedMs ( since: punctStart)
127+ let punctuationApplied = punctuatedText != correctedText
99128 logger. info ( " 标点处理后: \( punctuatedText, privacy: . public) " )
100129
101130 // Stage 5: Cloud rewrite
102131 Task { [ weak self] in
103132 guard let self else { return }
133+ let cloudRewriteStart = ContinuousClock . now
104134 let rewrittenText = await self . cloudRewriteService. rewriteOrPassthrough ( punctuatedText)
135+ let cloudRewriteMs = AnalyticsService . elapsedMs ( since: cloudRewriteStart)
105136 self . processingQueue. async {
137+ Self . trackPostProcessingCompleted (
138+ pipelineStart: pipelineStart,
139+ termNormMs: termNormMs, termNormChanged: termNormChanged,
140+ itnMs: itnMs, itnApplied: itnApplied,
141+ cscMs: cscMs, cscApplied: cscApplied,
142+ punctMs: punctMs, punctuationApplied: punctuationApplied,
143+ cloudRewriteMs: cloudRewriteMs
144+ )
106145 completion ( rewrittenText)
107146 }
108147 }
109148 }
110149 }
150+
151+ // MARK: - Analytics Helper
152+
153+ /// Build and send the PostProcessing.Completed event with per-stage latency.
154+ /// Single source of truth — called from both pipeline branches.
155+ private static func trackPostProcessingCompleted(
156+ pipelineStart: ContinuousClock . Instant ,
157+ termNormMs: Int , termNormChanged: Bool ,
158+ itnMs: Int , itnApplied: Bool ,
159+ cscMs: Int , cscApplied: Bool ,
160+ punctMs: Int , punctuationApplied: Bool ,
161+ cloudRewriteMs: Int
162+ ) {
163+ let totalMs = AnalyticsService . elapsedMs ( since: pipelineStart)
164+ AnalyticsService . track ( " PostProcessing.Completed " , parameters: [
165+ " itnApplied " : " \( itnApplied) " ,
166+ " cscApplied " : " \( cscApplied) " ,
167+ " punctuationApplied " : " \( punctuationApplied) " ,
168+ " totalLatencyMs " : " \( totalMs) " ,
169+ " termNormLatencyMs " : " \( termNormMs) " ,
170+ " itnLatencyMs " : " \( itnMs) " ,
171+ " cscLatencyMs " : " \( cscMs) " ,
172+ " punctLatencyMs " : " \( punctMs) " ,
173+ " cloudRewriteLatencyMs " : " \( cloudRewriteMs) " ,
174+ " termNormChanged " : " \( termNormChanged) " ,
175+ ] )
176+ }
111177}
0 commit comments