feat: нативное поле рассуждений (reasoning_field) — правка 400 от Console Go
Build LLM Proxy / Build and push (release) Successful in 39s
Build LLM Proxy / Build and push (release) Successful in 39s
Диагноз: апстрим deepseek-v4.1-flash (провайдер opencode, Console Go) в thinking-режиме требует reasoning_content в assistant-сообщениях с tool_calls, а клиент opencode присылает рассуждения как reasoning + reasoning_details. Отсюда 400 'The reasoning_content in the thinking mode must be passed back to the API'. - providers[].reasoning_field / reasoning_empty_ok: прокси аддитивно достраивает нативное поле в assistant-сообщениях с непустым tool_calls (текст из reasoning или reasoning_details[].text, тип reasoning.text); существующее непустое поле не перезаписывается, ничего не переименовывается, прочие сообщения не трогаются; - устойчивость разбора ответов к JSON-null (choices/delta/content/tool_calls) — было 290 фейловеров с локального апстрима на платные из-за нашего же исключения; - лог: класс исключения в сообщении об ошибке; для 4xx логируется тело ответа апстрима (читается безопасно: 4xx — не стрим); - тесты: ReasoningFieldTest (13), NullToleranceTest (6), ConfigLogicTest (+2) — 100 всего; - CONFIG.md, TESTING.md (фактические замеры A/B против Console Go).
This commit is contained in:
@@ -546,4 +546,41 @@ class ConfigLogicTest {
|
||||
assertEquals("off", effectiveThinkTags(upNull, prov(null)))
|
||||
assertEquals("off", effectiveThinkTags(upNull, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseConfigReadsReasoningFieldAndEmptyOkOnProvider() {
|
||||
val yaml = """
|
||||
providers:
|
||||
- id: p1
|
||||
url: "https://x.ru/api/v1"
|
||||
reasoning_field: reasoning_content
|
||||
reasoning_empty_ok: true
|
||||
- id: p2
|
||||
url: "https://y.ru/api/v1"
|
||||
reasoning_empty_ok: false
|
||||
models:
|
||||
- name: m1
|
||||
upstreams: []
|
||||
""".trimIndent()
|
||||
val cfg = parseConfig(Yaml.decodeYamlFromString(yaml))
|
||||
assertEquals("reasoning_content", cfg.providers[0].reasoning_field)
|
||||
assertEquals(true, cfg.providers[0].reasoning_empty_ok)
|
||||
assertEquals(null, cfg.providers[1].reasoning_field)
|
||||
assertEquals(false, cfg.providers[1].reasoning_empty_ok)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseConfigDefaultsReasoningFieldsWhenAbsent() {
|
||||
val yaml = """
|
||||
providers:
|
||||
- id: p1
|
||||
url: "https://x.ru/api/v1"
|
||||
models:
|
||||
- name: m1
|
||||
upstreams: []
|
||||
""".trimIndent()
|
||||
val cfg = parseConfig(Yaml.decodeYamlFromString(yaml))
|
||||
assertEquals(null, cfg.providers[0].reasoning_field)
|
||||
assertEquals(false, cfg.providers[0].reasoning_empty_ok)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user