From 6eb7ee044c31db3003538076c7760afe60ddd923 Mon Sep 17 00:00:00 2001 From: undivisible <136312656+undivisible@users.noreply.github.com> Date: Mon, 10 Aug 2026 05:05:11 +0000 Subject: [PATCH 1/5] Remove unused import process_voice_message_segment in chat.py Addressed Ruff warnings for unused imports and F811 function redefinitions in backend/routers/chat.py to improve code health. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- backend/routers/chat.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/routers/chat.py b/backend/routers/chat.py index 11831a02186..78594cf0895 100644 --- a/backend/routers/chat.py +++ b/backend/routers/chat.py @@ -27,7 +27,6 @@ from pydantic import BaseModel import database.chat as chat_db -import database.conversations as conversations_db import database.llm_usage as llm_usage_db from database.apps import record_app_usage from models.app import App, UsageHistoryType @@ -48,7 +47,6 @@ acquire_chat_session, emit_stream_error_fallback, initial_message_util, - process_voice_message_segment, process_voice_message_segment_stream, resolve_voice_message_language, transcribe_voice_message_segment, @@ -77,7 +75,7 @@ max_part_size, parse_multipart_form, ) -from utils.retrieval.graph import execute_graph_chat, execute_chat_stream, execute_persona_chat_stream +from utils.retrieval.graph import execute_chat_stream from utils.llm.usage_tracker import set_usage_context, reset_usage_context, Features from utils.users import get_user_display_name from utils.log_sanitizer import sanitize_pii @@ -1506,7 +1504,7 @@ def upload_file_chat( @router.post('/v1/files', response_model=List[FileChat], tags=['chat']) @max_part_size(CHAT_FILE_MAX_PART_SIZE) -def upload_file_chat( +def upload_file_chat_v1( files: List[UploadFile] = File(...), uid: str = Depends(auth.with_rate_limit(auth.get_current_user_uid, "file:upload")), ): @@ -1561,7 +1559,7 @@ def upload_file_chat( @router.post('/v1/messages/{message_id}/report', tags=['chat'], response_model=dict) -def report_message(message_id: str, uid: str = Depends(auth.get_current_user_uid)): +def report_message_v1(message_id: str, uid: str = Depends(auth.get_current_user_uid)): result = chat_db.get_message(uid, message_id) if result is None: raise HTTPException(status_code=404, detail='Message not found') @@ -1575,7 +1573,7 @@ def report_message(message_id: str, uid: str = Depends(auth.get_current_user_uid @router.delete('/v1/messages', tags=['chat'], response_model=Message) -def clear_chat_messages( +def clear_chat_messages_v1( plugin_id: Optional[str] = None, app_id: Optional[str] = None, uid: str = Depends(auth.get_current_user_uid) ): compat_app_id = app_id or plugin_id @@ -1607,7 +1605,7 @@ def clear_chat_messages( @router.post('/v1/initial-message', tags=['chat'], response_model=Message) -def create_initial_message( +def create_initial_message_v1( plugin_id: Optional[str] = None, app_id: Optional[str] = None, uid: str = Depends(auth.with_rate_limit(auth.get_current_user_uid, "chat:initial")), From 9f0be6333f7011d0ef5869e7603c0c62659331f9 Mon Sep 17 00:00:00 2001 From: undivisible <136312656+undivisible@users.noreply.github.com> Date: Mon, 10 Aug 2026 05:13:58 +0000 Subject: [PATCH 2/5] Update backend-routers.json for chat.py file size decrease Updating the product-file-line-count-ratchet baseline to reflect the smaller file size of backend/routers/chat.py. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .../backend-routers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json index 2654be78d9b..0b8261c1683 100644 --- a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json +++ b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json @@ -1,7 +1,7 @@ { "files": { "backend/routers/apps.py": 2425, - "backend/routers/chat.py": 1716, + "backend/routers/chat.py": 1714, "backend/routers/developer.py": 2282, "backend/routers/mcp_sse.py": 2009, "backend/routers/sync.py": 2083, From 8d8229ceba9d6e4c63e61c41f4bfa39aa483a20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Mon, 10 Aug 2026 15:14:53 +0800 Subject: [PATCH 3/5] fix: preserve legacy chat operation IDs Failure-Class: none --- backend/routers/chat.py | 28 ++++++++++++++++--- backend/tests/unit/test_chat_operation_ids.py | 20 +++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 backend/tests/unit/test_chat_operation_ids.py diff --git a/backend/routers/chat.py b/backend/routers/chat.py index 78594cf0895..3459af4f939 100644 --- a/backend/routers/chat.py +++ b/backend/routers/chat.py @@ -1502,7 +1502,12 @@ def upload_file_chat( # CLEANUP: Remove after new app goes to prod ---------------------------------------------------------- -@router.post('/v1/files', response_model=List[FileChat], tags=['chat']) +@router.post( + '/v1/files', + response_model=List[FileChat], + tags=['chat'], + operation_id='upload_file_chat_v1_files_post', +) @max_part_size(CHAT_FILE_MAX_PART_SIZE) def upload_file_chat_v1( files: List[UploadFile] = File(...), @@ -1558,7 +1563,12 @@ def upload_file_chat_v1( return response -@router.post('/v1/messages/{message_id}/report', tags=['chat'], response_model=dict) +@router.post( + '/v1/messages/{message_id}/report', + tags=['chat'], + response_model=dict, + operation_id='report_message_v1_messages__message_id__report_post', +) def report_message_v1(message_id: str, uid: str = Depends(auth.get_current_user_uid)): result = chat_db.get_message(uid, message_id) if result is None: @@ -1572,7 +1582,12 @@ def report_message_v1(message_id: str, uid: str = Depends(auth.get_current_user_ return {'message': 'Message reported'} -@router.delete('/v1/messages', tags=['chat'], response_model=Message) +@router.delete( + '/v1/messages', + tags=['chat'], + response_model=Message, + operation_id='clear_chat_messages_v1_messages_delete', +) def clear_chat_messages_v1( plugin_id: Optional[str] = None, app_id: Optional[str] = None, uid: str = Depends(auth.get_current_user_uid) ): @@ -1604,7 +1619,12 @@ def clear_chat_messages_v1( return initial_message_util(uid, compat_app_id) -@router.post('/v1/initial-message', tags=['chat'], response_model=Message) +@router.post( + '/v1/initial-message', + tags=['chat'], + response_model=Message, + operation_id='create_initial_message_v1_initial_message_post', +) def create_initial_message_v1( plugin_id: Optional[str] = None, app_id: Optional[str] = None, diff --git a/backend/tests/unit/test_chat_operation_ids.py b/backend/tests/unit/test_chat_operation_ids.py new file mode 100644 index 00000000000..1dca97db103 --- /dev/null +++ b/backend/tests/unit/test_chat_operation_ids.py @@ -0,0 +1,20 @@ +from fastapi.routing import APIRoute + +from routers.chat import router + + +def test_legacy_v1_chat_operation_ids_are_preserved_after_ruff_renames(): + operation_ids = { + (route.path, method): route.operation_id + for route in router.routes + if isinstance(route, APIRoute) + for method in route.methods + } + + assert operation_ids[('/v1/files', 'POST')] == 'upload_file_chat_v1_files_post' + assert ( + operation_ids[('/v1/messages/{message_id}/report', 'POST')] + == 'report_message_v1_messages__message_id__report_post' + ) + assert operation_ids[('/v1/messages', 'DELETE')] == 'clear_chat_messages_v1_messages_delete' + assert operation_ids[('/v1/initial-message', 'POST')] == 'create_initial_message_v1_initial_message_post' From 710dd03afcb087bd75947a17940dad4881d21730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Mon, 10 Aug 2026 20:45:18 +0800 Subject: [PATCH 4/5] ci: align chat router line-count baseline --- .../backend-routers.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json index 0b8261c1683..b5d445926b9 100644 --- a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json +++ b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json @@ -1,7 +1,7 @@ { "files": { "backend/routers/apps.py": 2425, - "backend/routers/chat.py": 1714, + "backend/routers/chat.py": 1734, "backend/routers/developer.py": 2282, "backend/routers/mcp_sse.py": 2009, "backend/routers/sync.py": 2083, @@ -10,7 +10,7 @@ }, "raise_justifications": { "backend/routers/apps.py": "POST /v1/apps/enable reads is_setup_completed through _setup_completed_from_response so a developer-controlled body that is non-JSON or not an object returns the intended 400 instead of a 500 (46 prod occurrences Jul 21-28); the guard stays in the route that owns the enable response contract. +5 keeps deleted-source rejection at the anonymous owner-migration admission boundary so a fresh UID cannot claim state from a fenced owner (SCA-280). +28 gives /v2/apps/search the name-OR-description predicate its `q` parameter documents plus the client-parity relevance tier, next to the one handler that owns them rather than importing search policy into utils/apps.py (#11292).", - "backend/routers/chat.py": "The HTTP and streaming PTT routes own the raw-audio and final-transcript boundaries; dev-only allowlisted capture therefore starts and persists at those two route seams while serialization, bounds, redaction, and export stay in testing.parity_pack_v0.live_capture. +33 emits done: from a staged typed answer when present and keeps the canned fallback when error: arrives without an answer so persona failures still finalize (SCA-296). +5 classifies staged typed stream-error answers as journey failure + shared fallback while keeping the typed done: copy (SCA-296). +35 keeps an in-memory typed done: frame when Firestore persistence fails so text clients are not left with only an earlier error: frame (SCA-296). +9 isolates post-persist app-usage analytics failures so they cannot mint a second client-visible message id (SCA-296).", + "backend/routers/chat.py": "The HTTP and streaming PTT routes own the raw-audio and final-transcript boundaries; dev-only allowlisted capture therefore starts and persists at those two route seams while serialization, bounds, redaction, and export stay in testing.parity_pack_v0.live_capture. +33 emits done: from a staged typed answer when present and keeps the canned fallback when error: arrives without an answer so persona failures still finalize (SCA-296). +5 classifies staged typed stream-error answers as journey failure + shared fallback while keeping the typed done: copy (SCA-296). +35 keeps an in-memory typed done: frame when Firestore persistence fails so text clients are not left with only an earlier error: frame (SCA-296). +9 isolates post-persist app-usage analytics failures so they cannot mint a second client-visible message id (SCA-296). +20 preserves the chat router's current line-count ratchet after the v1 operation-id pins (PR #11336).", "backend/routers/developer.py": "Developer memory create and batch routes must record their accepted write result at the authoritative API boundary; the shared dev-only allowlisted serializer/exporter lives in testing.parity_pack_v0.live_capture.", "backend/routers/mcp_sse.py": "The MCP SSE tool handler owns the successful memory-write response boundary; its one shared dev-only allowlisted capture call records accepted memory without duplicating serialization or export logic. +24 fences legacy-key, OAuth, and authorization-code issuance at the route's credential admission seams while offloading request validation, Firebase, and Firestore work to their owned executors (SCA-280). +42 keeps hosted MCP tool-call analytics at the authoritative execution boundary while the shared helper owns privacy filtering, PostHog delivery, and connector-ready operation mapping (SCA-294). +23 records every execution outcome and preserves authorization semantics independently of JSON-RPC error codes (SCA-294). +24 applies the account-cutover fence to authenticated MCP principals at their existing authorization boundary. +21 merges transcript-chunk hits into search_conversations and returns match_snippets so MCP search covers transcript evidence (#6621).", "backend/routers/sync.py": "Fresh Sync's daily ceiling remains beside the existing fresh-admission gates, before staged audio or durable worker work is created. Lifecycle-fenced workers are terminalized at this Cloud Tasks boundary so released WAL clients do not retry an owner they no longer own; +2 for the queued-dispatch-lost stale finalization path (#10033). +30 applies account-generation admission and background cutover fencing at the sync mutation and worker boundaries.", From 2d2c5feedc76cfd99804f14d2cbfaafcca842582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Thu, 13 Aug 2026 19:39:09 +0800 Subject: [PATCH 5/5] ci: align chat router line-count baseline after main merge --- .../backend-routers.json | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json index b5d445926b9..1ed8df2980a 100644 --- a/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json +++ b/.github/scripts/product_file_line_count_ratchet_baseline/backend-routers.json @@ -2,20 +2,18 @@ "files": { "backend/routers/apps.py": 2425, "backend/routers/chat.py": 1734, - "backend/routers/developer.py": 2282, - "backend/routers/mcp_sse.py": 2009, + "backend/routers/developer.py": 2064, + "backend/routers/mcp_sse.py": 1895, "backend/routers/sync.py": 2083, - "backend/routers/users.py": 2168, - "backend/routers/conversations.py": 1507 + "backend/routers/users.py": 2174 }, "raise_justifications": { "backend/routers/apps.py": "POST /v1/apps/enable reads is_setup_completed through _setup_completed_from_response so a developer-controlled body that is non-JSON or not an object returns the intended 400 instead of a 500 (46 prod occurrences Jul 21-28); the guard stays in the route that owns the enable response contract. +5 keeps deleted-source rejection at the anonymous owner-migration admission boundary so a fresh UID cannot claim state from a fenced owner (SCA-280). +28 gives /v2/apps/search the name-OR-description predicate its `q` parameter documents plus the client-parity relevance tier, next to the one handler that owns them rather than importing search policy into utils/apps.py (#11292).", "backend/routers/chat.py": "The HTTP and streaming PTT routes own the raw-audio and final-transcript boundaries; dev-only allowlisted capture therefore starts and persists at those two route seams while serialization, bounds, redaction, and export stay in testing.parity_pack_v0.live_capture. +33 emits done: from a staged typed answer when present and keeps the canned fallback when error: arrives without an answer so persona failures still finalize (SCA-296). +5 classifies staged typed stream-error answers as journey failure + shared fallback while keeping the typed done: copy (SCA-296). +35 keeps an in-memory typed done: frame when Firestore persistence fails so text clients are not left with only an earlier error: frame (SCA-296). +9 isolates post-persist app-usage analytics failures so they cannot mint a second client-visible message id (SCA-296). +20 preserves the chat router's current line-count ratchet after the v1 operation-id pins (PR #11336).", - "backend/routers/developer.py": "Developer memory create and batch routes must record their accepted write result at the authoritative API boundary; the shared dev-only allowlisted serializer/exporter lives in testing.parity_pack_v0.live_capture.", + "backend/routers/developer.py": "Developer memory create and batch routes must record their accepted write result at the authoritative API boundary; the shared dev-only allowlisted serializer/exporter lives in testing.parity_pack_v0.live_capture. +40 keeps category-filtered compatibility pagination at the route's released response boundary while all storage access remains in MemoryService.", "backend/routers/mcp_sse.py": "The MCP SSE tool handler owns the successful memory-write response boundary; its one shared dev-only allowlisted capture call records accepted memory without duplicating serialization or export logic. +24 fences legacy-key, OAuth, and authorization-code issuance at the route's credential admission seams while offloading request validation, Firebase, and Firestore work to their owned executors (SCA-280). +42 keeps hosted MCP tool-call analytics at the authoritative execution boundary while the shared helper owns privacy filtering, PostHog delivery, and connector-ready operation mapping (SCA-294). +23 records every execution outcome and preserves authorization semantics independently of JSON-RPC error codes (SCA-294). +24 applies the account-cutover fence to authenticated MCP principals at their existing authorization boundary. +21 merges transcript-chunk hits into search_conversations and returns match_snippets so MCP search covers transcript evidence (#6621).", "backend/routers/sync.py": "Fresh Sync's daily ceiling remains beside the existing fresh-admission gates, before staged audio or durable worker work is created. Lifecycle-fenced workers are terminalized at this Cloud Tasks boundary so released WAL clients do not retry an owner they no longer own; +2 for the queued-dispatch-lost stale finalization path (#10033). +30 applies account-generation admission and background cutover fencing at the sync mutation and worker boundaries.", - "backend/routers/users.py": "Account-deletion outcome telemetry at the users router boundary for PostHog ownership repair; repaired non-empty webhook URLs must also clear the persisted disabled state and failure health at the owning save boundary (#7519). +56 hosts the return-only AI user profile synthesis route at the boundary that already owns the ai-profile read/write contract; both prompt stages and the consolidation live in utils/llm/ai_user_profile.py. +5 moves the synthesis route onto the LLM executor and refuses a paywalled account before the provider call. +8 serves the primary-language picker list from GET /v1/users/available-languages so a language can be added without an app release; the response models live in models/users.py to keep the router growth to the route itself.", - "backend/routers/conversations.py": "The return-only conversation-topic route sits at the boundary that already owns conversation reads/writes; its prompt, model and parsing live in utils/llm/conversation_topic.py, and the entry only records the file's current size at the threshold. +3 defers the entitlement import into the handler so the router stays importable under the module-isolation tests." + "backend/routers/users.py": "Account-deletion outcome telemetry at the users router boundary for PostHog ownership repair; repaired non-empty webhook URLs must also clear the persisted disabled state and failure health at the owning save boundary (#7519). +56 hosts the return-only AI user profile synthesis route at the boundary that already owns the ai-profile read/write contract; both prompt stages and the consolidation live in utils/llm/ai_user_profile.py. +5 moves the synthesis route onto the LLM executor and refuses a paywalled account before the provider call. +8 serves the primary-language picker list from GET /v1/users/available-languages so a language can be added without an app release; the response models live in models/users.py to keep the router growth to the route itself. +6 aligns the Task assistant prompt request bound with the shipped desktop default at the existing assistant-settings schema boundary." }, "threshold": 1500 }