feat: вычисление сессии по истории (LCP) для providers[].session_header
- SHA-256 на commonMain + SessionRegistry (LCP по префикс-хэшам, LRU 1000/6ч, Mutex) - providers[].session_header: прокси сам ставит/перезаписывает заголовок сессии - цепочка хэшей стартует с первого user-сообщения (system не склеивает сессии) - CONFIG.md + тесты (SHA-256, LCP, LRU/TTL, парсинг, стабильность префиксов)
This commit is contained in:
@@ -73,6 +73,7 @@ providers:
|
||||
url: "https://routerai.ru/api/v1"
|
||||
key: "sk-..." # Bearer-ключ; можно подставлять из env
|
||||
max_concurrency: 4 # опционально; лимит по умолчанию для апстримов
|
||||
session_header: x-opencode-session # опционально; прокси считает сессию из истории
|
||||
patch: # уровень провайдера: ко всем его запросам
|
||||
provider:
|
||||
allow_fallbacks: false
|
||||
@@ -131,6 +132,39 @@ models:
|
||||
`patch` опционален на **любом** уровне (`providers` / `upstreams` / `models`):
|
||||
если ни одного нет — запрос проксируется как есть (исходное тело клиента).
|
||||
|
||||
### Сессия по истории (`session_header`)
|
||||
|
||||
`providers[].session_header` (опционально) — имя HTTP-заголовка, который прокси
|
||||
**вычисляет сам** из истории сообщений и ставит в запрос к этому провайдеру.
|
||||
Нужно для API, требующих стабильный идентификатор сессии (например,
|
||||
`x-opencode-session`), когда клиент его не шлёт или шлёт не то.
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
- id: some-provider
|
||||
url: "https://.../v1"
|
||||
session_header: x-opencode-session
|
||||
```
|
||||
|
||||
Как считается id:
|
||||
|
||||
1. Берётся финальное тело запроса (после всех `patch`), из него — `messages`.
|
||||
2. Цепочка **инкрементальных SHA-256 префикс-хэшей** начинается с первого
|
||||
`user`-сообщения (ведущий `system`-промпт игнорируется: он обычно одинаков
|
||||
у всех сессий клиента и как признак сессии бесполезен).
|
||||
3. В реестре сессий ищется **наибольший общий префикс** (LCP) с уже виденной
|
||||
историей. Нашли — используется id той сессии; не нашли — создаётся новая
|
||||
(`id` = хэш всей истории на первом ходу).
|
||||
4. Заголовок ставится **всегда** (клиентское значение перезаписывается).
|
||||
|
||||
Итог: пока история одной сессии растёт (дописываются assistant/user-сообщения),
|
||||
id не меняется; разные диалоги получают разные id.
|
||||
|
||||
> **Ограничения.** Реестр живёт в памяти (LRU: 1000 сессий / 6 часов) — при
|
||||
> рестарте прокси активные сессии получат новый id. Обрезка/суммаризация
|
||||
> истории рвёт общий префикс → сессия распадётся на новую. Диалоги с
|
||||
> одинаковым первым `user`-сообщением неразличимы (склеятся).
|
||||
|
||||
### Пример сборки тела (многослойный `patch`)
|
||||
|
||||
Берём модель `my-gpt` (из примера выше), маршрут уходит на апстрим
|
||||
@@ -211,6 +245,7 @@ data class ProviderConf(
|
||||
val key: String = "",
|
||||
val max_concurrency: Int? = null, // лимит по умолчанию для апстримов провайдера
|
||||
val patch: JsonObject? = null, // ко всем запросам провайдера
|
||||
val session_header: String? = null, // заголовок-сессия, считается из истории
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
Reference in New Issue
Block a user