1414from backend .core .request_logging import new_request_id , request_context , update_request_context
1515from backend .core .request_trace import log_test_prompt , prompt_tail
1616from backend .runtime .execution import build_tool_directive , build_usage_delta_factory , request_max_attempts , tool_directive_visible_text
17+ from backend .runtime .visible_text import VisibleTextSanitizer , sanitize_visible_text
1718from backend .services .attachment_preprocessor import preprocess_attachments
1819from backend .services .auth_quota import resolve_auth_context
1920from backend .services .client_profiles import detect_openai_client_profile
@@ -457,7 +458,7 @@ def build_responses_payload(
457458 output_text = visible_text
458459 content : list [dict [str , Any ]] = []
459460 if execution .state .reasoning_text :
460- content .append ({"type" : "reasoning_text" , "text" : execution .state .reasoning_text })
461+ content .append ({"type" : "reasoning_text" , "text" : sanitize_visible_text ( execution .state .reasoning_text ) })
461462 content .append ({"type" : "output_text" , "text" : output_text , "annotations" : []})
462463 payload ["output" ] = [{
463464 "id" : f"msg_{ uuid .uuid4 ().hex [:12 ]} " ,
@@ -490,7 +491,10 @@ def __init__(self, *, response_id: str, created: int, model_name: str, prompt: s
490491 self .started_text = False
491492 self .pending_chunks : list [str ] = []
492493 self .answer_fragments : list [str ] = []
494+ self .visible_answer_fragments : list [str ] = []
493495 self .reasoning_fragments : list [str ] = []
496+ self .answer_sanitizer = VisibleTextSanitizer ()
497+ self .reasoning_sanitizer = VisibleTextSanitizer ()
494498 self .tool_calls_emitted = False
495499
496500 def initial_chunks (self ) -> list [str ]:
@@ -523,6 +527,9 @@ def _ensure_text_item(self) -> None:
523527
524528 def on_delta (self , evt : dict [str , Any ], text_chunk : str | None , tool_calls : list [dict [str , Any ]] | None ) -> None :
525529 if text_chunk and evt .get ("phase" ) in ("think" , "thinking_summary" ):
530+ text_chunk = self .reasoning_sanitizer .feed (text_chunk )
531+ if not text_chunk :
532+ return
526533 self .reasoning_fragments .append (text_chunk )
527534 self .pending_chunks .append (_sse ("response.reasoning_text.delta" , {
528535 "response_id" : self .response_id ,
@@ -534,8 +541,12 @@ def on_delta(self, evt: dict[str, Any], text_chunk: str | None, tool_calls: list
534541 return
535542
536543 if text_chunk and evt .get ("phase" ) == "answer" :
537- self ._ensure_text_item ()
538544 self .answer_fragments .append (text_chunk )
545+ text_chunk = self .answer_sanitizer .feed (text_chunk )
546+ if not text_chunk :
547+ return
548+ self ._ensure_text_item ()
549+ self .visible_answer_fragments .append (text_chunk )
539550 self .pending_chunks .append (_sse ("response.output_text.delta" , {
540551 "response_id" : self .response_id ,
541552 "item_id" : self .message_id ,
@@ -553,6 +564,29 @@ def drain_pending(self) -> list[str]:
553564 self .pending_chunks = []
554565 return chunks
555566
567+ def _flush_visible_sanitizers (self ) -> None :
568+ reasoning_tail = self .reasoning_sanitizer .flush ()
569+ if reasoning_tail :
570+ self .reasoning_fragments .append (reasoning_tail )
571+ self .pending_chunks .append (_sse ("response.reasoning_text.delta" , {
572+ "response_id" : self .response_id ,
573+ "item_id" : self .message_id ,
574+ "output_index" : self .output_index ,
575+ "content_index" : self .content_index ,
576+ "delta" : reasoning_tail ,
577+ }))
578+ answer_tail = self .answer_sanitizer .flush ()
579+ if answer_tail :
580+ self ._ensure_text_item ()
581+ self .visible_answer_fragments .append (answer_tail )
582+ self .pending_chunks .append (_sse ("response.output_text.delta" , {
583+ "response_id" : self .response_id ,
584+ "item_id" : self .message_id ,
585+ "output_index" : self .output_index ,
586+ "content_index" : self .content_index ,
587+ "delta" : answer_tail ,
588+ }))
589+
556590 def emit_tool_calls (self , tool_calls : list [dict [str , Any ]]) -> None :
557591 for tool_call in tool_calls :
558592 block = {
@@ -595,6 +629,7 @@ def emit_tool_calls(self, tool_calls: list[dict[str, Any]]) -> None:
595629 self .tool_calls_emitted = True
596630
597631 def finalize (self , execution , directive ) -> list [str ]:
632+ self ._flush_visible_sanitizers ()
598633 chunks = self .drain_pending ()
599634 final_text = tool_directive_visible_text (
600635 directive ,
@@ -621,9 +656,10 @@ def finalize(self, execution, directive) -> list[str]:
621656 "delta" : final_text ,
622657 }))
623658 self .answer_fragments .append (final_text )
659+ self .visible_answer_fragments .append (final_text )
624660 chunks .extend (self .drain_pending ())
625661 elif directive .stop_reason != "tool_use" :
626- streamed_text = "" .join (self .answer_fragments )
662+ streamed_text = "" .join (self .visible_answer_fragments )
627663 if final_text .startswith (streamed_text ):
628664 missing_tail = final_text [len (streamed_text ):]
629665 if missing_tail :
@@ -635,6 +671,7 @@ def finalize(self, execution, directive) -> list[str]:
635671 "delta" : missing_tail ,
636672 }))
637673 self .answer_fragments .append (missing_tail )
674+ self .visible_answer_fragments .append (missing_tail )
638675
639676 if self .started_text :
640677 chunks .append (_sse ("response.output_text.done" , {
0 commit comments