From 46b89577eb390005190313a4844b7ae4a5bed119 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Wed, 26 Aug 2026 05:21:53 +0300 Subject: [PATCH] Preserve completed household answers in web chat --- cmd/creator-web/chat_test.go | 32 ++++++++++++++++++++++++++++++++ cmd/creator-web/server.go | 5 ++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/cmd/creator-web/chat_test.go b/cmd/creator-web/chat_test.go index 07c2e79..729659e 100644 --- a/cmd/creator-web/chat_test.go +++ b/cmd/creator-web/chat_test.go @@ -251,6 +251,38 @@ func TestChatMessageUsesLocalAdvisoryForOrdinaryConversationalQuestion(t *testin } } +func TestChatMessagePreservesCompletedCoreAnswerInsteadOfSecondAdvisory(t *testing.T) { + core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && r.URL.Path == "/v1/creator/sessions/current": + _ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"}) + case r.Method == http.MethodPost && r.URL.Path == "/v1/creator/chat/messages": + w.WriteHeader(http.StatusCreated) + _ = json.NewEncoder(w).Encode(interaction.ChatMessageResponse{ConversationID: "conversation-1", UtteranceID: "utterance-1", SpeechAct: interaction.SpeechActQuestion, NeedsCognition: true, FallbackText: "fallback", PlanningReply: "Готовый ответ ядра."}) + default: + t.Fatalf("unexpected core route %s %s", r.Method, r.URL.Path) + } + })) + defer core.Close() + cognitiveRuntime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fatal("completed core answer must not be replaced by a second advisory") + })) + defer cognitiveRuntime.Close() + handler, err := newHandler(core.URL, "http://continuity", "http://commit", core.Client(), cognitiveRuntime.URL) + if err != nil { + t.Fatal(err) + } + request := httptest.NewRequest(http.MethodPost, "/api/chat/messages", strings.NewReader(`{"message":"Обычный вопрос"}`)) + request.Header.Set("Authorization", "Bearer session-token") + request.Header.Set("Content-Type", "application/json") + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + var response browserChatResponse + if recorder.Code != http.StatusOK || json.NewDecoder(recorder.Body).Decode(&response) != nil || response.ReplyText != "Готовый ответ ядра." { + t.Fatalf("unexpected browser response: status=%d body=%s", recorder.Code, recorder.Body.String()) + } +} + func TestChatMessageReturnsHumanCapabilityMetadata(t *testing.T) { core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { diff --git a/cmd/creator-web/server.go b/cmd/creator-web/server.go index 5249318..fb633b6 100644 --- a/cmd/creator-web/server.go +++ b/cmd/creator-web/server.go @@ -480,7 +480,10 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) { saved.NeedsCognition = false } } - if saved.NeedsCognition && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired { + // Core owns the completed planning answer. The browser may ask for a local + // advisory only when core explicitly left the turn unanswered; otherwise a + // second model pass can replace a grounded result with a weaker one. + if saved.NeedsCognition && saved.PlanningReply == "" && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired { cognition = "unavailable" requestID, idErr := ids.New() if idErr == nil { -- 2.54.0