feat: флажок think_tags — вырезание think-тегов из ответа (split/strip)
Build LLM Proxy / Build and push (release) Successful in 24s
Build LLM Proxy / Build and push (release) Successful in 24s
Некоторые провайдеры (minimax) отдают рассуждения модели текстом внутри content, обернув их тегами think. Новое опциональное поле think_tags у providers[] и upstreams[] (приоритет: апстрим -> провайдер -> off, толерантный разбор true/false, неизвестное = off). - off: поведение не меняется (сырой байтовый passthrough в стриме); - split: блоки think вырезаются из content, текст уходит в reasoning_content; - strip: вырезаются и выбрасываются. Стрим разбирается построчно: отдельный ThinkTagSplitter на каждый choices[].index, хвост, который может оказаться началом разрезанного тега, не отдаётся до разрешения, на конце потока — finish(). Non-stream: тот же трансформ для message.content (и для результата rebuildFromChunks), уже существующий reasoning_content дописывается, а не теряется. Тесты: ThinkTagSplitterTest (7), разбор конфига и эффективный режим в ConfigLogicTest (2) — всего 42 теста, 0 падений. CONFIG.md + README.
This commit is contained in:
@@ -165,6 +165,59 @@ id не меняется; разные диалоги получают разн
|
||||
> истории рвёт общий префикс → сессия распадётся на новую. Диалоги с
|
||||
> одинаковым первым `user`-сообщением неразличимы (склеятся).
|
||||
|
||||
### Обработка think-тегов (`think_tags`)
|
||||
|
||||
Некоторые провайдеры (например, **minimax**) отдают рассуждения модели не в
|
||||
отдельном поле `reasoning_content`, а прямо в `content`, обернув их тегами
|
||||
`<think>…</think>`. Флажок `think_tags` (опционально) разрешает прокси разрезать
|
||||
такой ответ и разложить его по полям.
|
||||
|
||||
Поле доступно на двух уровнях:
|
||||
|
||||
- `providers[].think_tags` — правило по умолчанию для всех апстримов провайдера;
|
||||
- `upstreams[].think_tags` — необязательное переопределение на конкретной
|
||||
апстрим-модели.
|
||||
|
||||
Значения (строки):
|
||||
|
||||
| Значение | Поведение |
|
||||
|---|---|
|
||||
| `off` | дефолт: ответ не меняется, теги остаются в `content` |
|
||||
| `split` | блоки `<think>…</think>` вырезаются из `content`, их текст уходит в `reasoning_content` |
|
||||
| `strip` | блоки вырезаются и выбрасываются — клиент рассуждений не видит |
|
||||
|
||||
Разбор значения толерантный: `true` ≡ `split`, `false` ≡ `off`; любое
|
||||
неизвестное/пустое значение трактуется как `off` (прокси не падает).
|
||||
|
||||
Приоритет резолва — по убыванию специфичности: `upstreams[].think_tags` (самый
|
||||
конкретный) → `providers[].think_tags` → `off`. То есть значение апстрима
|
||||
перекрывает провайдерское.
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
- id: minimax
|
||||
url: "https://api.minimax.io/v1"
|
||||
key: "${MINIMAX_API_KEY}"
|
||||
think_tags: split # дефолт для всех апстримов провайдера
|
||||
|
||||
upstreams:
|
||||
- id: minimax-m1
|
||||
provider: minimax
|
||||
model: MiniMax-M1 # наследует split от провайдера
|
||||
|
||||
- id: minimax-text-only
|
||||
provider: minimax
|
||||
model: MiniMax-Text-01
|
||||
think_tags: strip # переопределение: рассуждения выбрасываем
|
||||
```
|
||||
|
||||
Работает и в стриме, и в обычном (non-stream) ответе. Тег может прийти
|
||||
разрезанным между чанками SSE — прокси держит хвост, который может оказаться
|
||||
началом тега, и не отдаёт его клиенту до разрешения, поэтому огрызок тега не
|
||||
утечёт. Незакрытый `<think>` в конце потока трактуется как «всё после него —
|
||||
рассуждения». Если `content` не строка (мультимодальный массив частей) — ответ
|
||||
не трогаем. Без флажка (`off`) ответ идёт байт-в-байт как раньше.
|
||||
|
||||
### Пример сборки тела (многослойный `patch`)
|
||||
|
||||
Берём модель `my-gpt` (из примера выше), маршрут уходит на апстрим
|
||||
|
||||
Reference in New Issue
Block a user