Activities for the synthesis workflow pipeline:
1. ChunkAndEmbedActivity — deterministic chunk ID + ingest via memory service
2. ExtractEntitiesActivity — wiki-link, proper noun, technical term extraction
3. ExtractFactsActivity — verb pattern matching (uses/runs/has/is/depends_on)
4. DetectContradictionsActivity — query existing facts + pre-filter contradictions
5. PersistSynthesisActivity — save entities + facts to memory service
Entity extraction patterns:
- [[WikiLinks]] → 0.95 confidence
- ProperNouns → 0.70 confidence
- TECHNICAL_TERMS/camelCase → 0.65 confidence
- Deduplication across patterns
Fact extraction:
- 6 verb patterns (uses, runs_on, has, is, depends_on, connects_to)
- Confidence boost when subject/object are known entities
Contradiction detection:
- Query memory service for existing facts about same subject
- Pre-filter: subject match + different object
- Severity: low/medium/high based on similarity score
- Auto-resolve low severity, queue review for medium/high
Tests: 10 pass (wiki links, proper nouns, tech terms, dedup,
verb patterns, empty text, contradicts, contains, common, classify)
Build: clean, 35 packages pass
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var testCtx = context.Background()
|
||||
|
||||
func TestExtractEntities_WikiLinks(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil) // No client needed for extraction
|
||||
entities, err := sa.ExtractEntitiesActivity(testCtx, "chunk-1", "Deploy [[Kubernetes]] with [[ArgoCD]]")
|
||||
assert.NoError(t, err)
|
||||
|
||||
names := entityNames(entities)
|
||||
assert.Contains(t, names, "Kubernetes")
|
||||
assert.Contains(t, names, "ArgoCD")
|
||||
|
||||
// Wiki links get high confidence
|
||||
for _, e := range entities {
|
||||
if e.Name == "Kubernetes" || e.Name == "ArgoCD" {
|
||||
assert.Equal(t, 0.95, e.Confidence)
|
||||
assert.Equal(t, "reference", e.EntityType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractEntities_ProperNouns(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil)
|
||||
entities, err := sa.ExtractEntitiesActivity(testCtx, "chunk-2", "Redis runs on Ubuntu Server")
|
||||
assert.NoError(t, err)
|
||||
|
||||
names := entityNames(entities)
|
||||
assert.Contains(t, names, "Redis")
|
||||
assert.Contains(t, names, "Ubuntu Server")
|
||||
}
|
||||
|
||||
func TestExtractEntities_TechnicalTerms(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil)
|
||||
entities, err := sa.ExtractEntitiesActivity(testCtx, "chunk-3", "Set MAX_RETRIES and use camelCase variables")
|
||||
assert.NoError(t, err)
|
||||
|
||||
names := entityNames(entities)
|
||||
assert.Contains(t, names, "MAX_RETRIES")
|
||||
assert.Contains(t, names, "camelCase")
|
||||
}
|
||||
|
||||
func TestExtractEntities_Deduplication(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil)
|
||||
entities, err := sa.ExtractEntitiesActivity(testCtx, "chunk-4", "[[Redis]] uses Redis for caching")
|
||||
assert.NoError(t, err)
|
||||
|
||||
count := 0
|
||||
for _, e := range entities {
|
||||
if e.Name == "Redis" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, 1, count, "Redis should appear only once")
|
||||
}
|
||||
|
||||
func TestExtractFacts_VerbPatterns(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil)
|
||||
entities := []ExtractedEntity{
|
||||
{Name: "Kubernetes", EntityType: "tool"},
|
||||
{Name: "Docker", EntityType: "tool"},
|
||||
}
|
||||
facts, err := sa.ExtractFactsActivity(testCtx, "chunk-5",
|
||||
"Kubernetes uses Docker for container runtime. Redis depends on TCP",
|
||||
entities)
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, len(facts), 0)
|
||||
|
||||
// Find the "uses" fact
|
||||
found := false
|
||||
for _, f := range facts {
|
||||
if f.Predicate == "uses" && f.Subject == "Kubernetes" {
|
||||
found = true
|
||||
assert.Greater(t, f.Confidence, 0.7) // Boosted by known entities
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "should find Kubernetes uses Docker fact")
|
||||
}
|
||||
|
||||
func TestExtractFacts_EmptyText(t *testing.T) {
|
||||
sa := NewSynthesisActivities(nil)
|
||||
facts, err := sa.ExtractFactsActivity(testCtx, "chunk-6", "", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, facts)
|
||||
}
|
||||
|
||||
func TestContradicts(t *testing.T) {
|
||||
assert.True(t, contradicts("Kubernetes uses port 8080", ExtractedFact{
|
||||
Subject: "Kubernetes", Predicate: "uses_port", Object: "9090",
|
||||
}))
|
||||
|
||||
assert.False(t, contradicts("Kubernetes uses port 8080", ExtractedFact{
|
||||
Subject: "Kubernetes", Predicate: "uses_port", Object: "8080",
|
||||
}))
|
||||
}
|
||||
|
||||
func TestContainsSubject(t *testing.T) {
|
||||
assert.True(t, containsSubject("Kubernetes runs on Linux", "kubernetes"))
|
||||
assert.False(t, containsSubject("Docker runs on Linux", "kubernetes"))
|
||||
}
|
||||
|
||||
func TestIsCommonWord(t *testing.T) {
|
||||
assert.True(t, isCommonWord("The"))
|
||||
assert.True(t, isCommonWord("This"))
|
||||
assert.False(t, isCommonWord("Kubernetes"))
|
||||
assert.False(t, isCommonWord("Redis"))
|
||||
}
|
||||
|
||||
func TestClassifyEntity(t *testing.T) {
|
||||
assert.Equal(t, "tool", classifyEntity("Kubernetes"))
|
||||
assert.Equal(t, "tool", classifyEntity("Docker"))
|
||||
assert.Equal(t, "tool", classifyEntity("Redis"))
|
||||
assert.Equal(t, "concept", classifyEntity("SomeRandomThing"))
|
||||
}
|
||||
|
||||
// helper
|
||||
func entityNames(entities []ExtractedEntity) []string {
|
||||
names := make([]string, len(entities))
|
||||
for i, e := range entities {
|
||||
names[i] = e.Name
|
||||
}
|
||||
return names
|
||||
}
|
||||
Reference in New Issue
Block a user