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:
@@ -494,4 +494,56 @@ class ConfigLogicTest {
|
||||
assertEquals("x-opencode-session", cfg.providers[0].session_header)
|
||||
assertEquals(null, cfg.providers[1].session_header)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseConfigReadsThinkTagsOnProviderAndUpstream() {
|
||||
val yaml = """
|
||||
providers:
|
||||
- id: p1
|
||||
url: "https://x.ru/api/v1"
|
||||
think_tags: split
|
||||
- id: p2
|
||||
url: "https://y.ru/api/v1"
|
||||
upstreams:
|
||||
- id: u1
|
||||
provider: p1
|
||||
model: real-1
|
||||
think_tags: strip
|
||||
- id: u2
|
||||
provider: p1
|
||||
model: real-2
|
||||
models:
|
||||
- name: m1
|
||||
upstreams: [u1, u2]
|
||||
""".trimIndent()
|
||||
|
||||
val cfg = parseConfig(Yaml.decodeYamlFromString(yaml))
|
||||
|
||||
assertEquals("split", cfg.providers[0].think_tags)
|
||||
assertEquals(null, cfg.providers[1].think_tags)
|
||||
assertEquals("strip", cfg.upstreams[0].think_tags)
|
||||
assertEquals(null, cfg.upstreams[1].think_tags)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun effectiveThinkTagsPrefersUpstreamThenProviderWithTolerantParse() {
|
||||
val upSplit = UpstreamConf("u1", "p", "m", think_tags = "split")
|
||||
val upNull = UpstreamConf("u2", "p", "m", think_tags = null)
|
||||
val prov = { tt: String? -> ProviderConf("p", "https://x", think_tags = tt) }
|
||||
|
||||
// значение у апстрима — берётся оно, провайдер игнорируется
|
||||
assertEquals("split", effectiveThinkTags(upSplit, null))
|
||||
assertEquals("split", effectiveThinkTags(upSplit, prov("true")))
|
||||
|
||||
// апстрим null — берётся провайдерский
|
||||
assertEquals("split", effectiveThinkTags(upNull, prov("split")))
|
||||
assertEquals("strip", effectiveThinkTags(upNull, prov("strip")))
|
||||
assertEquals("split", effectiveThinkTags(upNull, prov("true")))
|
||||
assertEquals("off", effectiveThinkTags(upNull, prov("false")))
|
||||
assertEquals("off", effectiveThinkTags(upNull, prov("yes")))
|
||||
|
||||
// нигде нет — "off"
|
||||
assertEquals("off", effectiveThinkTags(upNull, prov(null)))
|
||||
assertEquals("off", effectiveThinkTags(upNull, null))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user