145 lines
6.7 KiB
Markdown
145 lines
6.7 KiB
Markdown
# План правок по отчётам Опуса
|
|
|
|
> 31 мая 2026 · ветка `opus-fixes` · порядок: по критичности + зависимостям
|
|
|
|
---
|
|
|
|
## Этап 1. Сервер (`elmer/`) — 4 правки
|
|
|
|
### 1.1 🔴 `api/db.py` — WAL + закрытие соединений + request_id
|
|
**Файл**: `api/db.py`
|
|
**Строки**: класс `Database`, методы `_init_schema()`, `save_session()`
|
|
|
|
- [x] Добавить `PRAGMA journal_mode=WAL` и `busy_timeout=30000`
|
|
- [x] Добавить колонку `request_id TEXT UNIQUE` в `sessions`
|
|
- [x] Метод `close()` и контекстный менеджер (`__enter__`/`__exit__`)
|
|
- [x] `save_session()` — проверять `request_id` на дубликат, возвращать кэшированный диагноз
|
|
- [x] Индекс `idx_sessions_request_id`
|
|
|
|
### 1.2 🔴 `api/routes.py` — идемпотентность upload + /ping-llm без LLM
|
|
**Файл**: `api/routes.py`
|
|
**Строки**: `upload_session()`, `ping_llm()`
|
|
|
|
- [x] `upload_session()` — принимать `request_id` из JSON, возвращать кэш при дубликате
|
|
- [x] `upload_session()` — закрывать `db` через контекстный менеджер
|
|
- [x] `upload_session()` — не отдавать `str(e)` наружу, логировать, клиенту — обобщённый текст
|
|
- [x] `/ping-llm` — кэшировать результат на 60с, не вызывать LLM на каждый GET
|
|
|
|
### 1.3 🔴 `obd/protocol.py` — сброс буфера + не затирать ERROR
|
|
**Файл**: `obd/protocol.py`
|
|
**Строки**: `_write()`, `send()`
|
|
|
|
- [x] `_write()` — `self._ser.reset_input_buffer()` перед записью
|
|
- [x] `send()` — `if self._state == State.BUSY: self._state = State.READY` (не безусловно)
|
|
|
|
### 1.4 🟡 `brain/client.py` — таймаут из конфига + модель
|
|
**Файл**: `brain/client.py`
|
|
**Строки**: `Diagnoser.__init__()`, `Diagnoser.ask()`
|
|
|
|
- [x] `DEFAULT_MODEL` → `"gpt-oss-120b"`
|
|
- [x] `timeout` — параметр конструктора (по умолчанию 180)
|
|
- [x] Комментарии: убрать «DeepSeek»
|
|
|
|
---
|
|
|
|
## Этап 2. Сервер (`elmer/`) — улучшения (без 🔴 но важные)
|
|
|
|
### 2.1 🟡 `api/routes.py` — импорты наверх + кэш конфига
|
|
**Файл**: `api/routes.py`, `api/config.py`
|
|
|
|
- [x] Поднять импорты (`from brain.client import Diagnoser` и др.) на уровень модуля
|
|
- [x] `api/config.py` — `@lru_cache(maxsize=1)` на `load()`
|
|
|
|
### 2.2 🟡 `api/routes.py` — история /chat через роли
|
|
**Файл**: `api/routes.py`
|
|
**Строки**: `chat()`
|
|
|
|
- [x] Передавать историю как массив `messages` с ролями, а не строкой «Водитель:/Автоэксперт:»
|
|
|
|
### 2.3 🟡 `brain/client.py` — обработка ошибок LLM
|
|
**Файл**: `brain/client.py`
|
|
|
|
- [x] Различать `Timeout`, `HTTPError(429)`, `HTTPError(5xx)`, `HTTPError(4xx)`
|
|
- [x] Не отдавать детали исключения наружу
|
|
|
|
---
|
|
|
|
## Этап 3. Android (`elmer-android/`) — 6 правок
|
|
|
|
### 3.1 🔴 `ServerClient.kt` — request_id + идемпотентность
|
|
**Файл**: `app/src/main/java/ru/elmer/client/server/ServerClient.kt`
|
|
**Строки**: `uploadSession()`
|
|
|
|
- [ ] Генерировать `UUID` один раз до цикла ретраев
|
|
- [ ] Добавить `"request_id"` в JSON-тело
|
|
- [ ] Добавить заголовок `Idempotency-Key`
|
|
|
|
### 3.2 🔴 `ServerClient.kt` + `build.gradle.kts` — X-Api-Key
|
|
**Файлы**: `ServerClient.kt`, `app/build.gradle.kts`
|
|
|
|
- [ ] `build.gradle.kts` — `buildConfigField("String", "API_KEY", ...)`
|
|
- [ ] `ServerClient` — добавлять `X-Api-Key` во все запросы
|
|
- [ ] `MainActivity.sendToLlm()` и `startTest()` — тоже `X-Api-Key`
|
|
|
|
### 3.3 🔴 `ElmProtocol.kt` — не затирать ERROR + дренаж буфера
|
|
**Файл**: `app/src/main/java/ru/elmer/client/elm/ElmProtocol.kt`
|
|
**Строки**: `sendCommand()`, `write()`
|
|
|
|
- [ ] `sendCommand()` — `if (state == State.BUSY) state = State.READY`
|
|
- [ ] `write()` — `while (input.available() > 0) input.read()` перед записью
|
|
|
|
### 3.4 🔴 `SessionDb.kt` — безопасная миграция + индекс
|
|
**Файл**: `app/src/main/java/ru/elmer/client/db/SessionDb.kt`
|
|
**Строки**: `onUpgrade()`, `onCreate()`
|
|
|
|
- [ ] `onUpgrade()` — `ALTER TABLE` вместо `DROP TABLE`
|
|
- [ ] Индекс `idx_resp_session ON responses(session_id)`
|
|
|
|
### 3.5 🔴 `MainActivity.kt` — /ping-llm без LLM + двойной receiver + chatHistory
|
|
**Файл**: `app/src/main/java/ru/elmer/client/ui/MainActivity.kt`
|
|
**Строки**: `startTest()`, `sendToLlm()`, receiver-регистрация
|
|
|
|
- [ ] `startTest()` — троттлить `/ping-llm` (не чаще раза в 60с), предупреждать
|
|
- [ ] Убрать дублирующий receiver `statusReceiver` (оставить `scriptStatusReceiver`)
|
|
- [ ] `chatHistory` сохранять в `onSaveInstanceState` (JSON)
|
|
|
|
### 3.6 🔴 `ScriptRunnerService.kt` — null intent + мёртвый paused + try/finally
|
|
**Файл**: `app/src/main/java/ru/elmer/client/script/ScriptRunnerService.kt`
|
|
**Строки**: `onStartCommand()`, `executeScript()`
|
|
|
|
- [ ] `onStartCommand()` — `if (intent == null) { stopSelf(); return START_NOT_STICKY }`
|
|
- [ ] Убрать мёртвый флаг `paused` и `ACTION_RESUME` (или доделать паузу)
|
|
- [ ] `executeScript()` — `try/finally` вокруг `progress.stop()`
|
|
|
|
---
|
|
|
|
## Этап 4. Android (`elmer-android/`) — улучшения
|
|
|
|
### 4.1 🟡 `ServerClient.kt` — exponential backoff
|
|
**Файл**: `ServerClient.kt`
|
|
|
|
- [ ] `(1 shl (attempt-1)) * 1000 + Random.nextLong(0, 500)` вместо фиксированных 2000
|
|
|
|
### 4.2 🟡 `MainActivity.kt` — единый HTTP-клиент
|
|
**Файл**: `MainActivity.kt`
|
|
|
|
- [ ] `sendToLlm()` и `startTest()` перевести на OkHttp (через `ServerClient`)
|
|
|
|
### 4.3 🟡 `ScriptRunnerService.kt` — вынести хост в константу
|
|
**Файл**: `ScriptRunnerService.kt`, `MainActivity.kt`
|
|
|
|
- [ ] `obdai.ru` → `BuildConfig.SERVER_HOST` или константа
|
|
|
|
---
|
|
|
|
## Порядок выполнения
|
|
|
|
```
|
|
Этап 1 (сервер 🔴) → коммит
|
|
Этап 2 (сервер 🟡) → коммит
|
|
Этап 3 (Android 🔴) → коммит
|
|
Этап 4 (Android 🟡) → коммит
|
|
```
|
|
|
|
После каждого этапа — проверка: `python run.py` (сервер), сборка APK (Android).
|