* fix(H-337): configure Captain provider runtime * fix(captain): make knowledge rebuild atomic * fix(captain): scope retrieval provider failures --------- Co-authored-by: Rogee <rogee@ipao.vip>
88 lines
3.8 KiB
Go
88 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/gochat/gochat/internal/llm"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCaptainAssistantFAQRetrievalSeparatesEmptyAndFailures(t *testing.T) {
|
|
assistant := &model.CaptainAssistant{Name: "Fin", Status: model.AssistantStatusActive, Config: []byte(`{"feature_faq":true}`)}
|
|
history := []PlaygroundMessage{{Role: "user", Content: "How do refunds work?"}}
|
|
embedding := &llm.EmbeddingResponse{Data: []llm.EmbeddingData{{Embedding: []float64{0.1, 0.2, 0.3}}}}
|
|
|
|
t.Run("disabled FAQ skips retrieval and still allows chat", func(t *testing.T) {
|
|
provider := &mockLLMProvider{
|
|
embeddingError: errors.New("must not be called"),
|
|
chatResponse: &llm.ChatResponse{Choices: []llm.ChatChoice{{Message: llm.ChatMessage{Content: "Chat without FAQ."}}}},
|
|
}
|
|
repo := &mockResponseRepo{searchByEmbeddingError: errors.New("must not be called")}
|
|
svc := &CaptainAssistantService{responseRepo: repo, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
|
|
disabled := &model.CaptainAssistant{Name: "Fin", Status: model.AssistantStatusActive, Config: []byte(`{"feature_faq":false}`)}
|
|
|
|
content, err := svc.generatePlaygroundLLMResponse(context.Background(), disabled, history)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Chat without FAQ.", content)
|
|
assert.Zero(t, provider.embeddingCalls)
|
|
assert.Zero(t, repo.searchByEmbeddingCalls)
|
|
require.NotNil(t, provider.lastChatRequest)
|
|
})
|
|
|
|
t.Run("empty results still allow chat", func(t *testing.T) {
|
|
provider := &mockLLMProvider{
|
|
embeddingResponse: embedding,
|
|
chatResponse: &llm.ChatResponse{Choices: []llm.ChatChoice{{Message: llm.ChatMessage{Content: "No matching FAQ."}}}},
|
|
}
|
|
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
|
|
|
|
content, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "No matching FAQ.", content)
|
|
require.NotNil(t, provider.lastChatRequest)
|
|
assert.NotContains(t, provider.lastChatRequest.Messages[0].Content, "[FAQ 1]")
|
|
})
|
|
|
|
t.Run("embedding provider failure stops chat", func(t *testing.T) {
|
|
provider := &mockLLMProvider{embeddingError: &net.OpError{Op: "dial", Net: "tcp", Err: errors.New("connection refused")}}
|
|
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
|
|
|
|
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
|
|
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
|
|
assert.Nil(t, provider.lastChatRequest)
|
|
})
|
|
|
|
for name, response := range map[string]*llm.EmbeddingResponse{
|
|
"nil response": nil,
|
|
"empty data": {},
|
|
"empty first vector": {Data: []llm.EmbeddingData{{}}},
|
|
} {
|
|
t.Run(name+" stops chat", func(t *testing.T) {
|
|
provider := &mockLLMProvider{embeddingResponse: response}
|
|
svc := &CaptainAssistantService{responseRepo: &mockResponseRepo{}, llmProvider: provider, promptBuilder: NewSystemPromptBuilder()}
|
|
|
|
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
|
|
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
|
|
assert.Nil(t, provider.lastChatRequest)
|
|
})
|
|
}
|
|
|
|
t.Run("FAQ search failure stops chat", func(t *testing.T) {
|
|
provider := &mockLLMProvider{embeddingResponse: embedding}
|
|
svc := &CaptainAssistantService{
|
|
responseRepo: &mockResponseRepo{searchByEmbeddingError: errors.New("pgvector unavailable")},
|
|
llmProvider: provider,
|
|
promptBuilder: NewSystemPromptBuilder(),
|
|
}
|
|
|
|
_, err := svc.generatePlaygroundLLMResponse(context.Background(), assistant, history)
|
|
require.ErrorIs(t, err, ErrCaptainKnowledgeRetrieval)
|
|
assert.Nil(t, provider.lastChatRequest)
|
|
})
|
|
}
|