From 15133cd0fc76c25412babd03262052660fc8a7dc Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 24 Aug 2026 19:22:25 +0300 Subject: [PATCH] fix: keep local chat context in lifeboat --- adapters/models/openai_compatible_local.go | 17 +++++++++-- .../models/openai_compatible_local_test.go | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/adapters/models/openai_compatible_local.go b/adapters/models/openai_compatible_local.go index 63d391c..e8a1253 100644 --- a/adapters/models/openai_compatible_local.go +++ b/adapters/models/openai_compatible_local.go @@ -122,10 +122,10 @@ type compatibleChatResponse struct { } func (a *OpenAICompatibleLocalAdapter) Advise(ctx context.Context, request cognitive.AdvisoryRequest) (cognitive.AdvisoryResponse, error) { - if err := ValidateAdvisoryRequest(request); err != nil || request.ProviderModelID == "" || len(request.Context) != 0 { + if err := ValidateAdvisoryRequest(request); err != nil || request.ProviderModelID == "" { return cognitive.AdvisoryResponse{}, ErrInvalidAdvisory } - payload := compatibleChatRequest{Model: string(request.ProviderModelID), Messages: []compatibleMessage{{Role: "system", Content: "Provide non-canonical advisory text only. Never claim that an action occurred."}, {Role: "user", Content: request.Message}}, Temperature: 0, Stream: false, MaxTokens: request.MaxOutputBytes / 4, ChatTemplateKwargs: compatibleTemplateKwargs{EnableThinking: false}} + payload := compatibleChatRequest{Model: string(request.ProviderModelID), Messages: []compatibleMessage{{Role: "system", Content: "Provide non-canonical advisory text only. Never claim that an action occurred."}, {Role: "user", Content: localAdvisoryMessage(request)}}, Temperature: 0, Stream: false, MaxTokens: request.MaxOutputBytes / 4, ChatTemplateKwargs: compatibleTemplateKwargs{EnableThinking: false}} var result compatibleChatResponse status, err := a.requestJSON(ctx, http.MethodPost, "/chat/completions", payload, &result) if err != nil { @@ -151,6 +151,19 @@ func (a *OpenAICompatibleLocalAdapter) Advise(ctx context.Context, request cogni return response, nil } +func localAdvisoryMessage(request cognitive.AdvisoryRequest) string { + if len(request.Context) == 0 { + return request.Message + } + var builder strings.Builder + builder.WriteString(request.Message) + builder.WriteString("\n\nRelevant local context:\n") + for _, excerpt := range request.Context { + fmt.Fprintf(&builder, "- [%s/%s] %s\n", excerpt.SourceKind, excerpt.SourceID, excerpt.Text) + } + return builder.String() +} + func (a *OpenAICompatibleLocalAdapter) requestJSON(ctx context.Context, method, path string, input, output any) (int, error) { var body io.Reader if input != nil { diff --git a/adapters/models/openai_compatible_local_test.go b/adapters/models/openai_compatible_local_test.go index 5f3f1f5..39ab6cc 100644 --- a/adapters/models/openai_compatible_local_test.go +++ b/adapters/models/openai_compatible_local_test.go @@ -92,3 +92,32 @@ func TestOpenAICompatibleLocalAdapterDisablesTemplateThinkingForBoundedAdvisory( t.Fatal("expected bounded advisory to disable template thinking") } } + +func TestOpenAICompatibleLocalAdapterPassesLocalContextToAdvisory(t *testing.T) { + transport := &captureRoundTripper{} + adapter, err := NewOpenAICompatibleLocalAdapter("http://gateway/v1", transport) + if err != nil { + t.Fatal(err) + } + _, err = adapter.Advise(context.Background(), cognitive.AdvisoryRequest{ + Version: cognitive.CreatorAdvisoryV1, RequestID: "request-context", ActorID: "actor", PrincipalID: "principal", ScopeID: "scope", + Message: "Продолжи разговор", Locale: "ru-RU", MaxOutputBytes: 256, ProviderBindingID: "binding-1", ProviderBindingRevision: 1, + ProviderInstanceID: "provider-1", ModelDescriptorID: "model-1", ProviderModelID: capability.ProviderModelID("local-model"), + Context: []cognitive.ContextExcerpt{{ID: "focus", SourceKind: "situation", SourceID: "focus-1", Revision: 1, Sensitivity: cognitive.SensitivityPrivateAllowed, Text: "Активный проект: Домовой"}}, + }) + if err != nil { + t.Fatal(err) + } + var payload struct { + Messages []struct { + Role string `json:"role"` + Content string `json:"content"` + } `json:"messages"` + } + if err := json.Unmarshal(transport.body, &payload); err != nil { + t.Fatal(err) + } + if len(payload.Messages) != 2 || !strings.Contains(payload.Messages[1].Content, "Активный проект: Домовой") { + t.Fatalf("local context was not passed to advisory: %+v", payload.Messages) + } +} -- 2.54.0