Consolidate config: wire grok-4.3 as the editor with an explicit reasoning_effort:none off-switch (control:effort), keeping DeepSeek's echo-mine no-op, and mark escalation stubs interim

This commit is contained in:
Claude (backend session) 2026-07-05 20:58:09 +03:00
parent 84737d032c
commit f4e7124c4b
6 changed files with 90 additions and 16 deletions

View file

@ -3,7 +3,6 @@
#
# Источники проверки 2026-07-04 (воркфлоу-фактчек, официальные страницы):
# DeepSeek https://api-docs.deepseek.com/quick_start/pricing
# Anthropic https://platform.claude.com/docs/en/about-claude/pricing
# Z.AI/GLM https://docs.z.ai/guides/overview/pricing
# Kimi https://platform.kimi.ai/docs/pricing/chat-k26
# xAI https://docs.x.ai/docs/models (Grok 4.1 Fast retired 15.05.2026!)
@ -107,14 +106,17 @@ models:
# не верифицировали, а для редактора (черновик — свежий вход на чанк) cache-hit≈0,
# поэтому консервативно не моделируем скидку — потолок не слепнет. Уточнит полигон.
price: { input_per_m: 1.25, cached_per_m: 1.25, cache_write_per_m: 0, output_per_m: 2.50 }
# Дефолт-редактор (bake-off эксп-04): thinking OFF (0 reasoning-токенов, ~13с), temperature шлём.
# Дефолт-редактор (bake-off эксп-04): temperature шлём; reasoning OFF даётся ТОЛЬКО явным
# reasoning_effort:"none" (control:effort + off_effort:none). Дефолт grok = low → омиссия
# заставляет думать: проба eval 05.07 (Chat Completions) — omission→444 reasoning-ток., "none"→0.
# control:none (off-by-omission) молча оставил бы low; control:effort/off_effort шлёт явный none.
capabilities:
temperature: { mode: send }
reasoning: { control: none }
reasoning: { control: effort, off_effort: none }
note: >-
Дефолт-редактор SFW (D3, bake-off эксп-04) + главный пермиссив-тир канала B + судья 18+.
reasoning additive: think-ON только после резерва в EstimateUSD (D6.2/техдолг F3). Заведена,
в стадию не включена — переключение editor→grok в следующем конфиг-шаге (после подтверждения прод-ключа).
reasoning additive: think-ON только после резерва в EstimateUSD (D6.2/техдолг F3). Включена
в edit-стадию C1/C2 (D1/D3, консолидация 05.07); прод-прогон — только с ЧИСТЫМ xAI-ключом без data-sharing.
# claude-sonnet-5 / claude-opus-4-8 удалены вместе с провайдером anthropic
# (см. комментарий у providers выше). Судья Фазы 2 — Gemini (нативный адаптер).

View file

@ -41,7 +41,10 @@ stages:
reasoning: "off"
- name: edit
role: editor
model: glm-5
# Дефолт-редактор — grok-4.3 (D1/D3, bake-off эксп-04; glm-5 был плейсхолдером).
# reasoning:"off" → grok шлёт ЯВНЫЙ reasoning_effort:"none" (control:effort/off_effort:none,
# models.yaml) — омиссия у grok = low. Прод: только чистый xAI-ключ без data-sharing.
model: grok-4.3
prompt: ../prompts/editor.md
prompt_version: v0-draft
temperature: 0.4
@ -61,10 +64,12 @@ gates:
escalation:
chains:
# Anthropic убран из стека (владелец, 04.07). Провизорная не-Anthropic
# эскалация — Kimi K2.6; финальную модель эскалации (после отказа от
# Anthropic — кандидаты GLM-5.1/5.2, Gemini) выбирает оркестратор к Фазе 1.
# В Фазе 0 эскалация не исполняется (гейты off).
# ⚠️ ИНТЕРИМ-ЗАГЛУШКА, НЕ контракт. Ратифицированная D3-цепочка:
# канал A: deepseek-v4-pro → glm-5.1 → gemini-3.1-pro; канал B: grok-4.3.
# Эти модели и провайдеры gemini/openai ещё НЕ заведены в models.yaml (цены/ключи +
# пересчёт cost-model полигоном гейтят заводку) → пока провизорный kimi-k2.6.
# Заводка D3-цепочки — отдельный шаг. В Фазе 0 / при gates off эскалация не
# исполняется (CheckKeys не требует её ключей, пока budget_usd=0).
default: [kimi-k2.6]
adult: [kimi-k2.6]
# Адресный премиум-бюджет книги, $. Формула «≤2×» из Р5 неоднозначна после

View file

@ -40,7 +40,10 @@ stages:
reasoning: "off"
- name: edit
role: editor
model: glm-5
# Дефолт-редактор — grok-4.3 (D1/D3, bake-off эксп-04; glm-5 был плейсхолдером).
# reasoning:"off" → grok шлёт ЯВНЫЙ reasoning_effort:"none" (control:effort/off_effort:none).
# (Стадия select выше — judge/glm-5, Gemini-слот Фазы 2; её НЕ трогаем.)
model: grok-4.3
prompt: ../prompts/editor.md
prompt_version: v0-draft
temperature: 0.4
@ -58,7 +61,10 @@ gates:
escalation:
chains:
default: [kimi-k2.6] # Anthropic убран; финальная модель эскалации — оркестратор, Фаза 1
# ⚠️ ИНТЕРИМ-ЗАГЛУШКА, НЕ контракт. Ратифицированная D3: канал A deepseek-v4-pro →
# glm-5.1 → gemini-3.1-pro, канал B grok-4.3 (эти модели/провайдеры gemini/openai в
# models.yaml не заведены — цены/ключи; заводка — отдельный шаг). Провизорный kimi-k2.6.
default: [kimi-k2.6]
adult: [kimi-k2.6]
budget_usd: 0

View file

@ -164,3 +164,46 @@ func TestBoevoyConfigDeepSeekThinkingStaysOn(t *testing.T) {
t.Fatalf("%s re-arms the echo mine: %s", model, why)
}
}
// TestBoevoyConfigGrokEditorExplicitNone pins the consolidation fix (tasks 1-3): grok-4.3
// resolves to an EXPLICIT reasoning off-switch (ReasoningEffortField + OffEffort "none"), NOT
// off-by-omission — grok's OMITTED default is "low" (it thinks, billed additively per xAI, so
// control:none would silently cost ~444 reasoning tokens/edit). It must not trip the echo-mine
// guard (xAI is not echo-prone: the editor input is a Russian draft), and the edit stage of both
// boevoy pipelines must be grok-4.3, while c2's select (judge) stage stays the glm-5 placeholder.
func TestBoevoyConfigGrokEditorExplicitNone(t *testing.T) {
m, err := LoadModels(filepath.Join("..", "..", "configs", "models.yaml"))
if err != nil {
t.Fatalf("load boevoy models.yaml: %v", err)
}
const model = "grok-4.3"
cap := m.ResolveCapability(model)
if cap.Reasoning.Control != llm.ReasoningEffortField || cap.Reasoning.OffEffort != "none" {
t.Errorf("%s must resolve to explicit reasoning_effort:\"none\" (control=effort, off_effort=none), got control=%q off_effort=%q — control:none leaves grok's \"low\" default (thinks, additive billing)",
model, cap.Reasoning.Control, cap.Reasoning.OffEffort)
}
if why := m.echoMineViolation(model); why != "" {
t.Errorf("%s must not trip the echo-mine guard (xAI editor input is a Russian draft, not CJK): %s", model, why)
}
for _, pf := range []string{"pipeline-c1.yaml", "pipeline-c2.yaml"} {
p, err := LoadPipeline(filepath.Join("..", "..", "configs", pf), m)
if err != nil {
t.Fatalf("load %s: %v", pf, err)
}
var editModel, selectModel string
for _, st := range p.Stages {
switch st.Role {
case "editor":
editModel = st.Model
case "judge":
selectModel = st.Model
}
}
if editModel != "grok-4.3" {
t.Errorf("%s edit stage model = %q, want grok-4.3 (D1/D3 default; glm-5 was the placeholder)", pf, editModel)
}
if pf == "pipeline-c2.yaml" && selectModel != "glm-5" {
t.Errorf("%s select (judge) stage must stay glm-5 (Gemini Phase-2 slot, untouched by task 3), got %q", pf, selectModel)
}
}
}

View file

@ -50,9 +50,15 @@ type ReasoningControl string
const (
// ReasoningNone is the OpenAI-compat baseline: low|medium|high go out as
// reasoning_effort; off and "" emit nothing (the provider's own default).
// Fits models where thinking is off by omission (DeepSeek / xAI-Grok on the
// Phase-1 draft/edit path).
// reasoning_effort; off and "" emit nothing, leaving the provider's OWN default.
// Use it for a model whose thinking must NOT be pushed off that default from our
// side: DeepSeek-flash on the draft path defaults to thinking ON, and "off" here is
// a deliberate NO-OP (emitting nothing keeps it ON) — DISABLING DeepSeek thinking
// arms the echo mine (it returns the untranslated CJK source at HTTP 200;
// config.echoMineViolation fail-fasts on it). This is the WRONG control for a model
// whose thinking is ON BY OMISSION: xAI-Grok defaults reasoning_effort to "low" (it
// thinks), so a role that must NOT think (the editor) uses ReasoningEffortField with
// OffEffort "none" to send an EXPLICIT reasoning_effort:"none" instead.
ReasoningNone ReasoningControl = "none"
// ReasoningEffortField maps "off" to a fixed OffEffort value on the
// reasoning_effort field (gpt-5: "minimal"; local ollama: "none").

View file

@ -84,6 +84,18 @@ func TestCapabilityWireBody(t *testing.T) {
mustStr(t, m, "reasoning_effort", "minimal")
},
},
{
// The grok-4.3 editor money-path (consolidation tasks 1-2): reasoning=off must send an
// EXPLICIT reasoning_effort:"none" — grok's OMITTED default is "low" (it thinks, billed
// additively). A test that only checked the param is ABSENT would pass on the buggy
// control:none wiring; this pins the param is PRESENT and equals "none" (eval's flagged hole).
name: "grok editor: control=effort off_effort=none sends explicit reasoning_effort:none",
cap: Capability{Reasoning: ReasoningCap{Control: ReasoningEffortField, OffEffort: "none"}},
effort: "off",
check: func(t *testing.T, m map[string]any) {
mustStr(t, m, "reasoning_effort", "none")
},
},
{
name: "gemini mandatory: swallow off, never disable, temperature sent",
cap: Capability{Reasoning: ReasoningCap{Control: ReasoningMandatory}},
@ -162,7 +174,7 @@ func TestCapabilityWireBody(t *testing.T) {
extra: map[string]any{"max_tokens": 999, "custom": "x"},
check: func(t *testing.T, m map[string]any) {
mustNum(t, m, "max_tokens", 4096) // resolved wins
mustStr(t, m, "custom", "x") // gap filled
mustStr(t, m, "custom", "x") // gap filled
},
},
}