WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
package chatpipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/config"
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
"github.com/Tencent/WeKnora/internal/types/interfaces"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Phase two's claim is that memory changes what gets retrieved, not only what
|
||||
// the answer prompt says. These tests hold the two places that has to be true:
|
||||
// the query the retriever is given, and the order documents come back in.
|
||||
|
||||
type stubRetrievalMemory struct {
|
||||
stubMemoryService
|
||||
|
||||
retrieval interfaces.RetrievalContext
|
||||
affinity map[string]int
|
||||
askedFor []string
|
||||
}
|
||||
|
||||
func (s *stubRetrievalMemory) RetrievalContextFor(context.Context) interfaces.RetrievalContext {
|
||||
return s.retrieval
|
||||
}
|
||||
|
||||
func (s *stubRetrievalMemory) DocumentAffinity(_ context.Context, ids []string) map[string]int {
|
||||
s.askedFor = ids
|
||||
return s.affinity
|
||||
}
|
||||
|
||||
func TestWhoIsAskingReachesTheQueryRewriter(t *testing.T) {
|
||||
memoryService := &stubRetrievalMemory{
|
||||
retrieval: interfaces.RetrievalContext{
|
||||
Background: "在做医学影像的后端",
|
||||
Interests: []string{"医学影像分割"},
|
||||
Documents: []string{"分割模型调参手册"},
|
||||
Items: []*types.MemoryItem{
|
||||
{ID: "m1", Kind: types.MemoryKindProfile, Content: "在做医学影像的后端"},
|
||||
},
|
||||
},
|
||||
}
|
||||
plugin := &PluginQueryUnderstand{
|
||||
memoryService: memoryService,
|
||||
config: &config.Config{Conversation: &config.ConversationConfig{
|
||||
RewritePromptSystem: "改写用户的问题。",
|
||||
RewritePromptUser: "{{query}}",
|
||||
}},
|
||||
}
|
||||
|
||||
chatManage := &types.ChatManage{}
|
||||
chatManage.Query = "分割怎么调参"
|
||||
|
||||
_, userPrompt := plugin.buildPrompts(t.Context(), chatManage, nil)
|
||||
|
||||
require.Contains(t, userPrompt, "在做医学影像的后端",
|
||||
"the same question means different things to different people, and only "+
|
||||
"the rewriter can act on that before retrieval runs")
|
||||
require.Contains(t, userPrompt, "医学影像分割")
|
||||
require.Contains(t, userPrompt, "分割模型调参手册")
|
||||
require.Contains(t, userPrompt, "分割怎么调参", "the question itself must survive")
|
||||
|
||||
// Conditioning the rewriter is not a recall. The background is fed in
|
||||
// whole, relevant or not, so counting it as "memories this answer used"
|
||||
// would report unrelated memories on every single turn. That list is
|
||||
// MEMORY_RECALL's to build, from what the question actually matched.
|
||||
require.Empty(t, chatManage.UsedMemories)
|
||||
}
|
||||
|
||||
func TestQueryRewriterIsUnchangedWithoutMemory(t *testing.T) {
|
||||
plugin := &PluginQueryUnderstand{
|
||||
memoryService: &stubRetrievalMemory{},
|
||||
config: &config.Config{Conversation: &config.ConversationConfig{
|
||||
RewritePromptSystem: "改写用户的问题。",
|
||||
RewritePromptUser: "{{query}}",
|
||||
}},
|
||||
}
|
||||
chatManage := &types.ChatManage{}
|
||||
chatManage.Query = "分割怎么调参"
|
||||
|
||||
_, userPrompt := plugin.buildPrompts(t.Context(), chatManage, nil)
|
||||
require.NotContains(t, userPrompt, "asker_background")
|
||||
require.Empty(t, chatManage.UsedMemories)
|
||||
}
|
||||
|
||||
func TestFamiliarDocumentsRankHigher(t *testing.T) {
|
||||
memoryService := &stubRetrievalMemory{affinity: map[string]int{"doc-familiar": 8}}
|
||||
plugin := &PluginMemoryAffinity{memoryService: memoryService}
|
||||
|
||||
chatManage := &types.ChatManage{
|
||||
PipelineState: types.PipelineState{RerankResult: []*types.SearchResult{
|
||||
{ID: "c1", KnowledgeID: "doc-stranger", Score: 0.80},
|
||||
{ID: "c2", KnowledgeID: "doc-familiar", Score: 0.78},
|
||||
}},
|
||||
}
|
||||
|
||||
err := plugin.OnEvent(t.Context(), types.CHUNK_RERANK, chatManage, func() *PluginError {
|
||||
return nil
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "c2", chatManage.RerankResult[0].ID,
|
||||
"between two comparable passages, prefer the document this person works from")
|
||||
}
|
||||
|
||||
func TestAnUnrelatedDocumentIsNotDraggedToTheTop(t *testing.T) {
|
||||
// The signal is weak — it says the retriever kept picking a document, not
|
||||
// that the user found it useful — so it must never overturn a clear
|
||||
// relevance gap.
|
||||
memoryService := &stubRetrievalMemory{affinity: map[string]int{"doc-familiar": 1000}}
|
||||
plugin := &PluginMemoryAffinity{memoryService: memoryService}
|
||||
|
||||
chatManage := &types.ChatManage{
|
||||
PipelineState: types.PipelineState{RerankResult: []*types.SearchResult{
|
||||
{ID: "c1", KnowledgeID: "doc-relevant", Score: 0.90},
|
||||
{ID: "c2", KnowledgeID: "doc-familiar", Score: 0.40},
|
||||
}},
|
||||
}
|
||||
|
||||
err := plugin.OnEvent(t.Context(), types.CHUNK_RERANK, chatManage, func() *PluginError {
|
||||
return nil
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "c1", chatManage.RerankResult[0].ID)
|
||||
}
|
||||
|
||||
func TestRerankIsUntouchedWithoutAffinity(t *testing.T) {
|
||||
plugin := &PluginMemoryAffinity{memoryService: &stubRetrievalMemory{}}
|
||||
chatManage := &types.ChatManage{
|
||||
PipelineState: types.PipelineState{RerankResult: []*types.SearchResult{
|
||||
{ID: "c1", KnowledgeID: "doc-a", Score: 0.80},
|
||||
{ID: "c2", KnowledgeID: "doc-b", Score: 0.78},
|
||||
}},
|
||||
}
|
||||
err := plugin.OnEvent(t.Context(), types.CHUNK_RERANK, chatManage, func() *PluginError {
|
||||
return nil
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0.80, chatManage.RerankResult[0].Score)
|
||||
require.Equal(t, 0.78, chatManage.RerankResult[1].Score)
|
||||
}
|
||||
Reference in New Issue
Block a user