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

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
@@ -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)
}