0.167.46
Added
- Reasoning-поля end-to-end + thinking-переключатели запроса ( — P3, аудит 2026-08-context). Цепочка рассуждений reasoning-моделей больше не теряется: разбор → живой стриминг → персист → UI. Провайдеры:
parseOpenAIResponse/ChatStream OpenAI-compat извлекаютreasoning_content(DeepSeek/Qwen/MiniMax/vLLM, приоритет) иreasoning(OpenRouter) из SDK ExtraFields (типизированных полей в openai-go v1.12 нет; unknown-ключи SDK хранит со статусом invalid — присутствие детектится черезRaw(), неValid()); стриминг эмититStreamEvent{Type:"reasoning_delta"}(ctx-guarded send — потребитель, переставший читать, не подвешивает горутину) и собирает текст вLLMResponse.ReasoningContent. Anthropic:thinking_deltaдополнительно к echo-back-буферам (#277) эмититreasoning_deltaдля живого отображения. Fallback-цепочка форвардитreasoning_deltaс пометкой committed (видимый вывод неотменим при failover). Реестр:ProviderSpec.ThinkingStyle—thinking_type(DeepSeekthinking:{type:enabled|disabled}) иenable_thinking(Qwen/DashScope, гибридные модели; wire-форматы сверены с доками провайдеров июль 2026);ChatRequest.ThinkingLevelпрокидывается изAgentRunSpec(applyThinkingLevel) и транслируется в wire-параметр; level="" ничего не отправляет (ручнойextra_bodyостаётся рабочим), а явныйextra_body-override того же ключа выигрывает у реестрового переключателя (обратная совместимость: thinking_level агента по умолчанию “standard”, и занятый ключ молча выкинул бы pre-#306 DeepSeek-конфиги). Транспорт:AgentHook.OnReasoningStream(+Nop/Composite/все реализации); runner прокидывает reasoning-дельты в hook и пишет reasoning tool-итераций в live-персист; actor публикует_reasoning_delta(отдельный от_stream_deltaфлаг — коалесцер менеджера каналов не смешивает мышление с телом ответа), финальный персистmessages.reasoning(колонка существует с миграции 003, всегда NULL; reasoning-only финал при пустом Content тоже персистится); WS: eventreasoning_delta,ReasoningTextв конверте, reasoning-flavour терминатора (_reasoning_end) — payload reasoning-терминатора не может утечь в answer-слотText; resume-снапшотagent_status.reasoning_text(единый снимок состояния под одной блокировкой — без torn snapshot). Коалесцер: два независимых потока дельт, foreign-сообщения в очереди pendingMsgs. Компакция: reasoning НЕ входит в сводки (инвариант — тестыmessagesToText/archiveToConversation); echo-back reasoning в OpenAI-запросы не выполняется (DeepSeek принимаетreasoning_contentтолько в Chat Prefix Completion beta). WebUI:streamingReasoningв ChatStore (санитизация bidi/zero-width, кап 256 KiB на дельтах и снапшотах, watchdog-очистка), свёрнутый блок «Размышления» (ReasoningBlock: plain-text в live-фазе → markdown после стрима; ручное сворачивание уважается до конца цикла стрима, сброс latch на новом цикле; focus-visible a11y), resume блока при реконнекте, reasoning прикрепляется к сообщению наstream_end. llmlog:reasoning_contentответа логируется для всех OpenAI-compat провайдеров. Тесты (+35): parse-приоритеты/null/non-string, стриминг (reasoning-дельты, отсутствие regressions), thinkingExtras-матрица и buildParams-переключатели (включая extra_body-override), fallback forward/commit, anthropic-эмит, runner hook/FinalReasoning/live-персист, streamHook-публикация, coalesce-изоляция потоков + сохранность foreign, SendDelta-маршрутизация (комбинированные флаги), компакция-инварианты, store-тесты фронтенда и компонентные тесты ReasoningBlock (5 сценариев).