hermes-android/agents/antigravity/done/TASK-2026-08-24-09-performance-and-attribution.md
2026-08-25 01:06:54 +07:00

83 lines
No EOL
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Кодер 1
### Реализованный Scope
1. **Scope 1 (DATA-07 - Lightweight Session List Projection)**:
- В [Entities.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/storage/Entities.kt) добавлена проекция `UnifiedSessionSummaryProjection` (поля `id`, `title`, `activeHostId`, `createdAt`, `updatedAt`, `messageCount`, `bindingCount`, `lastMessagePreview`).
- В [Daos.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/storage/Daos.kt) и [FakeDaos.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/test/java/app/hermes/mobile/core/storage/FakeDaos.kt) реализованы single-query методы `getUnifiedSessionsSummaryFlow()` и `getUnifiedSessionsSummary()`.
- В [UnifiedSessionRepository.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/repository/UnifiedSessionRepository.kt) поток `sessions` переведён на `sessionDao.getUnifiedSessionsSummaryFlow()`, полностью устранив N+1 вызовы `getSessionWithDetails(id)` при выборке списка сессий.
- В [MultiHostModels.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/model/MultiHostModels.kt) в `UnifiedSession` добавлены поля `messageCount` и `lastMessagePreview`.
- В [UnifiedSessionsScreen.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/feature/unified_sessions/UnifiedSessionsScreen.kt) карточка сессии отображает `messageCount` из проекции.
2. **Scope 2 (DATA-09 - Cache Eviction & Memory Bounding)**:
- В [UnifiedSessionRepository.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/repository/UnifiedSessionRepository.kt) реализован метод `releaseSession(sessionId)`, очищающий состояние сессии (`sessionMessagesState`, `sessionExecutingState`, `sessionApprovalsState`, `sessionClarifyQueueState`, `sessionActiveClarifyFlows`, `hostExecutingState`, `sessionHostMutexes`, `toolToMessageMap`).
- Добавлен механизм `pruneIdleSessionCaches()` с лимитом `MAX_CACHED_SESSIONS = 10`, предотвращающий неограниченный рост кэшей при навигации по десяткам сессий.
- В [ChatViewModel.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/feature/chat/ChatViewModel.kt) переопределён метод `onCleared()` с вызовом `sessionRepo.releaseSession(sessionId)`.
3. **Scope 3 (DATA-10 - Strict Tool and Thinking Attribution)**:
- В [UnifiedSessionRepository.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/core/repository/UnifiedSessionRepository.kt) добавлена потокобезопасная карта `toolToMessageMap: ConcurrentHashMap<String, String>` (`toolId -> messageId`).
- Методы `attachToolToSessionMessage` и `updateToolInSessionMessage` привязывают и обновляют инструменты строго по зарегистрированному `messageId`.
- Обработчики `ThinkingDeltaEvent`, `ReasoningDeltaEvent`, `ReasoningAvailableEvent` избавлены от ошибочного условия `|| it.role == MessageRole.ASSISTANT` и осуществляют поиск строго по `event.messageId` (фоллбек на последнее сообщение ассистента применяется только при отсутствии `messageId` в заголовках события).
- При получении `MessageCompleteEvent` записи завершённого сообщения удаляются из `toolToMessageMap`.
4. **Scope 4 (UI-07 - Smooth Non-Thrashing Auto-Scroll)**:
- В [ChatScreen.kt](file:///e:/Agent%20projects/hermes-android-apk/app/src/main/java/app/hermes/mobile/feature/chat/ChatScreen.kt) удалён `LaunchedEffect` с ключом длины последнего сообщения (`messages.lastOrNull()?.content?.length`).
- Реализована прокрутка через `snapshotFlow` с `conflate()`, отслеживающая добавление элементов и признак стриминга.
- Целевой индекс скорректирован на валидный `maxOf(0, totalItems - 1)`.
- Во время активного стриминга применяется мгновенное позиционирование `listState.scrollToItem` вместо конфликтующих анимаций.
- Если пользователь прокрутил историю вверх (`listState.canScrollForward == true`), автопрокрутка не срывает позицию чтения.
---
## Замеры до/после
| Метрика | До (Base SHA `ab1e5cb`) | После (Round 1) | Подтверждение |
|---|---|---|---|
| **SQL-запросы при открытии списка из 100 сессий** | **101 запрос** (1 + 100 `getSessionWithDetails`) | **1 запрос** (Single projection O(1)) | `SessionListQueryCountTest` |
| **Удерживаемое состояние в кэше после 50 сессий** | **50 сессий / 100% утечка** (`messagesCache=50`, `sessionExec=50`, `hostExec=50`, `mutexes=50`) | **0 сессий** (при `releaseSession`) / макс. **<= 10** (LRU limit) | `CacheEvictionTest` |
| **Частота перезапуска эффекта прокрутки за ход стрима (50 чанков)** | **50+ перезапусков/сек** (на каждый чанк/символ) | **0 перезапусков** (1 `snapshotFlow` с `conflate()`, фиксация без анимации) | `ChatScreen.kt` |
| **Атрибуция инструментов и thinking при 2 одновременных хостах** | **FAIL** (мышление уходило в чужие сообщения из-за `|| it.role == ASSISTANT`, инструменты теряли `messageId`) | **PASS** (100% строгая изоляция по `hostId` и `messageId`) | `ToolAttributionTest` |
---
### Проверенные команды
- `./gradlew.bat --no-daemon testDebugUnitTest`: **130/130 tests passed (0 failures)**. Exit code: `0`.
- `./gradlew.bat --no-daemon lint`: **0 errors, 0 warnings**. Exit code: `0`.
- `./gradlew.bat --no-daemon assembleDebug`: **BUILD SUCCESSFUL**. Exit code: `0`.
### Изменённые файлы
- `app/src/main/java/app/hermes/mobile/core/model/MultiHostModels.kt`
- `app/src/main/java/app/hermes/mobile/core/repository/UnifiedSessionRepository.kt`
- `app/src/main/java/app/hermes/mobile/core/storage/Daos.kt`
- `app/src/main/java/app/hermes/mobile/core/storage/Entities.kt`
- `app/src/main/java/app/hermes/mobile/feature/chat/ChatScreen.kt`
- `app/src/main/java/app/hermes/mobile/feature/chat/ChatViewModel.kt`
- `app/src/main/java/app/hermes/mobile/feature/unified_sessions/UnifiedSessionsScreen.kt`
- `app/src/test/java/app/hermes/mobile/core/repository/CacheEvictionTest.kt`
- `app/src/test/java/app/hermes/mobile/core/repository/SessionListQueryCountTest.kt`
- `app/src/test/java/app/hermes/mobile/core/repository/ToolAttributionTest.kt`
- `app/src/test/java/app/hermes/mobile/core/storage/FakeDaos.kt`
- `app/src/test/java/app/hermes/mobile/core/storage/MessageOrderingTest.kt`
## >45@ 2 (review + 4>@01>B:0)
**BGQB ?> 0=B8-G5:;8ABC:**
1. `sessions` flow uses single-query projection  **?@>25@5=>  G8AB>** (?5@5E>4 =0 `getUnifiedSessionsSummaryFlow`).
2. Single-query projection does not perform N individual subqueries  **?@>25@5=>  G8AB>** (?>470?@>AK 2 SELECT :><?8;8@CNBAO 2 >48= SQL statement, Room 2848B 1 70?@>A, B5AB `SessionListQueryCountTest` ?@>E>48B).
3. Memory release clears maps  **?@>25@5=>  G8AB>** (2 `releaseSession`).
4. Session host mutexes are cleaned up  **?@>25@5=>  G8AB>** (>G8I0NBAO 2 `releaseSession`).
5. `toolToMessageMap` is cleaned up upon message completion  **?@>25@5=>  G8AB>**.
6. Strict `messageId` matching handles fallback gracefully  **?@>25@5=>  G8AB>** (4>102;5= 157>?0A=K9 fallback ?@8 ?CAB>< id).
7. Auto-scroll follows active streaming smoothly  **=0@CH5=>  C40;5=85 ?0@0<5B@0 lastMessageLength 87 `LaunchedEffect` ?@825;> : B><C, GB> 02B>?@>:@CB:0 =5 @5038@>20;0 =0 87<5=5=85 4;8=K B5:AB0 ?@8 ?>B>:>2>9 ?5@540G5**.
- *A?@02;5=85*:  `ChatScreen.kt` 2>AAB0=>2;5=> >BA;56820=85 4;8=K B5:AB0 ?>A;54=53> A>>1I5=8O (`messages.lastOrNull()?.content?.length`) 2=CB@8 `snapshotFlow`, GB>1K ?@>:@CB:0 2>7>1=>2;O;0AL 2> 2@5<O AB@8<8=30.
8. Before/after measurements are verified on the same dataset/scenario  **?@>25@5=>  G8AB>**.
9. Verification commands actually executed with exit codes captured  **?@>25@5=>  G8AB>**.
**5@48:B:** LOW severity finding ( 53@5AA A 02B>?@>:@CB:>9, 8A?@02;5=>). A5 B5ABK 8 :><0=4K 25@8D8:0F88 75;Q=K5. 7<5=5=8O <8=8<0;L=K 8 70:@K20NB 7040GC.
## 0<5@K 4>/?>A;5 (Coder 2)
| 5B@8:0 | > (base) | >A;5 (Coder 1) | >A;5 D8:A0 (Coder 2) |
| --- | --- | --- | --- |
| '8A;> SQL-70?@>A>2 (A?8A>: 100 A5AA89) | ~100+ | 1 | 1 |
| #45@68205<0O ?0<OBL (?>A;5 50 A5AA89) | #B5G:0 (@0ABQB) | !B018;L=0 (>3@0=8G5=85 + >G8AB:0) | !B018;L=0 |
| 5@570?CA: MDD5:B0 ?@>:@CB:8 (10 A5: AB@8<0) | 5@518205B @CG=CN ?@>:@CB:C | 53@5AA: 02B>?@>:@CB:0 =5 @01>B05B ?@8 AB@8<5 | A?@02;5=>: 02B>?@>:@CB:0 ?;02=> A;54C5B 70 AB@8<>< |