## Phase 8: Enterprise Extractor Improvements ✅ - 14 security extractors (TLS, JWT, SQL injection, XSS, etc.) - 10 framework-specific extractors (Spring, Django, Rails, etc.) - Config file security detection (YAML, TOML) ## Phase 9: Autonomous Extractor Generation ✅ - Shadow mode executor with TP/FP tracking - Graduation pipeline with confidence thresholds - Auto-rollback on regression detection - Cross-project pattern syncing ## UAT Suite Complete (14 scripts, 90 tests) - test-core-detection.sh (6 tests) - test-declarative-extractors.sh (5 tests) - test-domain-frameworks.sh (5 tests) - test-domain-unreal.sh (3 tests) - test-llm-extraction.sh (6 tests) - test-eval-harness.sh (5 tests) - test-cross-language.sh (3 tests) - test-precommit-performance.sh (4 tests) - test-output-formats.sh (8 tests) - test-drift-detection.sh (6 tests) - test-exit-codes.sh (12 tests) + 3 more scripts ## Other Changes - Updated roadmap to mark Phase 8-9 complete - Added .gitignore entries for build artifacts - Updated pre-commit: 800 line limit, exclude tests/data/cmd Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
642 lines
21 KiB
Go
642 lines
21 KiB
Go
// Package main seeds StemeDB with demo data using realistic agent identities.
|
|
//
|
|
// This program creates properly signed assertions for all 5 demo scenarios
|
|
// using pre-generated keypairs from demo/keys/agents.json.
|
|
//
|
|
// Usage:
|
|
//
|
|
// go run ./cmd/demo-seed
|
|
// go run ./cmd/demo-seed --api-url http://localhost:18180
|
|
// go run ./cmd/demo-seed --keys-file ./demo/keys/agents.json --verbose
|
|
//
|
|
// The demo scenarios are:
|
|
// 1. Conflicting Claims - FDA vs Reddit on gastroparesis risk
|
|
// 2. Cascade Invalidation - Source retraction affecting assertions
|
|
// 3. Audit Trail - Agent query logging
|
|
// 4. Time Decay - Historical vs current market status
|
|
// 5. Trust & Safety - Quarantine and circuit breaker triggers
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/orchard9/stemedb-go/steme"
|
|
)
|
|
|
|
const (
|
|
defaultBaseURL = "http://localhost:18180"
|
|
defaultKeysFile = "demo/keys/agents.json"
|
|
)
|
|
|
|
// DemoAgent represents a demo agent from agents.json.
|
|
type DemoAgent struct {
|
|
Seed string `json:"seed"`
|
|
PublicKey string `json:"public_key"`
|
|
Tier int `json:"tier"`
|
|
Description string `json:"description"`
|
|
Sources string `json:"sources"`
|
|
}
|
|
|
|
// AgentRegistry holds all demo agents.
|
|
type AgentRegistry struct {
|
|
agents map[string]DemoAgent
|
|
signers map[string]*steme.Signer
|
|
}
|
|
|
|
func main() {
|
|
// Parse flags
|
|
apiURL := flag.String("api-url", "", "StemeDB API URL (default: $STEMEDB_API_URL or http://localhost:18180)")
|
|
keysFile := flag.String("keys-file", "", "Path to agents.json (default: demo/keys/agents.json)")
|
|
verbose := flag.Bool("verbose", false, "Enable verbose output")
|
|
flag.Parse()
|
|
|
|
// Resolve API URL
|
|
url := *apiURL
|
|
if url == "" {
|
|
url = os.Getenv("STEMEDB_API_URL")
|
|
}
|
|
if url == "" {
|
|
url = defaultBaseURL
|
|
}
|
|
|
|
// Resolve keys file path
|
|
keysPath := *keysFile
|
|
if keysPath == "" {
|
|
// Try to find keys file relative to working directory
|
|
keysPath = findKeysFile()
|
|
}
|
|
|
|
// Load agents
|
|
registry, err := loadAgents(keysPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load agents: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
fmt.Println("=== StemeDB Demo Data Seeder ===")
|
|
fmt.Println()
|
|
fmt.Printf("API URL: %s\n", url)
|
|
fmt.Printf("Keys File: %s\n", keysPath)
|
|
fmt.Println()
|
|
|
|
// Print agent mapping
|
|
fmt.Println("Demo Agents:")
|
|
for name, agent := range registry.agents {
|
|
fmt.Printf(" %s (T%d) -> %s...\n", name, agent.Tier, agent.PublicKey[:16])
|
|
}
|
|
fmt.Println()
|
|
|
|
// Verify server is running using FDA agent
|
|
fdaSigner := registry.signers["fda:drug-label-ingestor"]
|
|
client := steme.NewClient(url, fdaSigner)
|
|
|
|
health, err := client.Health(ctx)
|
|
if err != nil {
|
|
log.Fatalf("Server not reachable at %s: %v\nStart the server with: cargo run --package stemedb-api", url, err)
|
|
}
|
|
fmt.Printf("Server Status: %s (v%s, %d assertions)\n", health.Status, health.Version, health.AssertionsCount)
|
|
fmt.Println()
|
|
|
|
// Register all demo sources BEFORE creating assertions
|
|
fmt.Println("=== Registering Demo Sources ===")
|
|
registered := 0
|
|
for name, src := range Sources {
|
|
err := registerSource(ctx, url, name, src)
|
|
if err != nil {
|
|
log.Printf(" Warning: %s: %v", name, err)
|
|
} else {
|
|
registered++
|
|
}
|
|
}
|
|
fmt.Printf(" Registered %d sources\n\n", registered)
|
|
|
|
// Populate all 5 demo scenarios
|
|
demo1Hashes := populateDemo1ConflictingClaims(ctx, registry, url, *verbose)
|
|
sourceHash := populateDemo2SourceRetraction(ctx, registry, url, *verbose)
|
|
agentID := populateDemo3AuditTrail(ctx, registry, url, *verbose)
|
|
populateDemo4TimeDecay(ctx, registry, url, *verbose)
|
|
populateDemo5TrustSafety(ctx, registry, url, *verbose)
|
|
|
|
// P2.2: Populate Drug Conflict Scenarios (150+ assertions across 3 drugs)
|
|
populateDrugConflicts(ctx, registry, url)
|
|
|
|
// Wait for ingestion
|
|
fmt.Println("Waiting for async ingestion...")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Print all demo curl commands
|
|
fmt.Println()
|
|
fmt.Println("========================================")
|
|
fmt.Println("=== DEMO CURL COMMANDS ===")
|
|
fmt.Println("========================================")
|
|
fmt.Println()
|
|
|
|
printDemo1Commands(demo1Hashes)
|
|
printDemo2Commands(sourceHash)
|
|
printDemo3Commands(agentID)
|
|
printDemo4Commands()
|
|
printDemo5Commands()
|
|
printConflictDemoCommands()
|
|
printHistoricalDemoCommands()
|
|
printCascadeDemoCommands()
|
|
|
|
fmt.Println("========================================")
|
|
fmt.Println("=== AGENT ID REFERENCE ===")
|
|
fmt.Println("========================================")
|
|
fmt.Println()
|
|
fmt.Println("When viewing data in the dashboard, use this mapping:")
|
|
fmt.Println()
|
|
for name, agent := range registry.agents {
|
|
fmt.Printf(" %s...\n -> %s (T%d)\n", agent.PublicKey[:24], name, agent.Tier)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
// findKeysFile locates the agents.json file.
|
|
func findKeysFile() string {
|
|
// Try current directory
|
|
if _, err := os.Stat(defaultKeysFile); err == nil {
|
|
return defaultKeysFile
|
|
}
|
|
|
|
// Try one level up (if running from cmd/demo-seed)
|
|
upPath := filepath.Join("..", "..", defaultKeysFile)
|
|
if _, err := os.Stat(upPath); err == nil {
|
|
return upPath
|
|
}
|
|
|
|
// Default to standard path
|
|
return defaultKeysFile
|
|
}
|
|
|
|
// loadAgents loads agent configurations from agents.json.
|
|
func loadAgents(path string) (*AgentRegistry, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read %s: %w", path, err)
|
|
}
|
|
|
|
var agents map[string]DemoAgent
|
|
if err := json.Unmarshal(data, &agents); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %w", path, err)
|
|
}
|
|
|
|
signers := make(map[string]*steme.Signer)
|
|
for name, agent := range agents {
|
|
signer, err := steme.NewSignerFromHex(agent.Seed)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create signer for %s: %w", name, err)
|
|
}
|
|
signers[name] = signer
|
|
}
|
|
|
|
return &AgentRegistry{
|
|
agents: agents,
|
|
signers: signers,
|
|
}, nil
|
|
}
|
|
|
|
// Demo1Hashes holds the assertion hashes for Demo 1.
|
|
type Demo1Hashes struct {
|
|
FDA string
|
|
Anecdotal string
|
|
}
|
|
|
|
// populateDemo1ConflictingClaims creates conflicting assertions about gastroparesis risk.
|
|
func populateDemo1ConflictingClaims(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) Demo1Hashes {
|
|
fmt.Println("=== Demo 1: Populating Conflicting Claims ===")
|
|
|
|
var hashes Demo1Hashes
|
|
|
|
// FDA/Regulatory (Tier 0): Low risk from clinical trials
|
|
fdaSigner := registry.signers["fda:drug-label-ingestor"]
|
|
fdaClient := steme.NewClient(baseURL, fdaSigner)
|
|
|
|
fdaAssertion := steme.NewAssertion("semaglutide:gastroparesis_risk", "risk_level").
|
|
WithText("Low incidence in clinical trials (0.2%)").
|
|
WithConfidence(0.95).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassRegulatory).
|
|
WithSourceHash("0da1111111111111111111111111111111111111111111111111111111110da1").
|
|
Build()
|
|
|
|
hash, err := fdaClient.Assert(ctx, fdaAssertion)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create FDA assertion: %v", err)
|
|
} else {
|
|
hashes.FDA = hash
|
|
fmt.Printf(" fda:drug-label-ingestor (T0): %s\n", hash[:16]+"...")
|
|
}
|
|
|
|
// Anecdotal (Tier 5): High patient-reported incidence
|
|
redditSigner := registry.signers["reddit:health-discussion-scraper"]
|
|
redditClient := steme.NewClient(baseURL, redditSigner)
|
|
|
|
anecdotalAssertion := steme.NewAssertion("semaglutide:gastroparesis_risk", "risk_level").
|
|
WithText("High patient-reported incidence (see r/Ozempic)").
|
|
WithConfidence(0.70).
|
|
WithLifecycle(steme.LifecycleProposed).
|
|
WithSourceClass(steme.SourceClassAnecdotal).
|
|
WithSourceHash("5555555555555555555555555555555555555555555555555555555555555555").
|
|
Build()
|
|
|
|
hash, err = redditClient.Assert(ctx, anecdotalAssertion)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create anecdotal assertion: %v", err)
|
|
} else {
|
|
hashes.Anecdotal = hash
|
|
fmt.Printf(" reddit:health-discussion-scraper (T5): %s\n", hash[:16]+"...")
|
|
}
|
|
|
|
fmt.Println()
|
|
return hashes
|
|
}
|
|
|
|
// populateDemo2SourceRetraction creates assertions for cascade invalidation demo.
|
|
func populateDemo2SourceRetraction(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) string {
|
|
fmt.Println("=== Demo 2: Populating Source for Retraction Demo ===")
|
|
|
|
// Use PubMed agent for clinical trial data
|
|
pubmedSigner := registry.signers["pubmed:abstract-indexer"]
|
|
client := steme.NewClient(baseURL, pubmedSigner)
|
|
|
|
// Use the actual hash from the registered CARDIOVASC_MEGA_TRIAL source
|
|
// This ensures "Preview Impact" on the source page will find these assertions
|
|
sourceHash := Sources["CARDIOVASC_MEGA_TRIAL"].Hash()
|
|
|
|
fmt.Printf(" Source (NEJM Study): %s\n", sourceHash[:16]+"...")
|
|
|
|
// Create assertions citing this source
|
|
assertion1 := steme.NewAssertion("GLP1_Agonists", "cardiovascular_benefit").
|
|
WithBoolean(true).
|
|
WithConfidence(0.92).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassClinical).
|
|
WithSourceHash(sourceHash).
|
|
Build()
|
|
|
|
hash, err := client.Assert(ctx, assertion1)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create CV benefit assertion: %v", err)
|
|
} else {
|
|
fmt.Printf(" pubmed:abstract-indexer (T1): %s (CV benefit)\n", hash[:16]+"...")
|
|
}
|
|
|
|
assertion2 := steme.NewAssertion("GLP1_Agonists", "mortality_reduction").
|
|
WithText("20% reduction in cardiovascular mortality").
|
|
WithConfidence(0.88).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassClinical).
|
|
WithSourceHash(sourceHash).
|
|
Build()
|
|
|
|
hash, err = client.Assert(ctx, assertion2)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create mortality assertion: %v", err)
|
|
} else {
|
|
fmt.Printf(" pubmed:abstract-indexer (T1): %s (mortality reduction)\n", hash[:16]+"...")
|
|
}
|
|
|
|
fmt.Println()
|
|
return sourceHash
|
|
}
|
|
|
|
// populateDemo3AuditTrail creates assertions and queries to generate audit trail data.
|
|
func populateDemo3AuditTrail(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) string {
|
|
fmt.Println("=== Demo 3: Populating Audit Trail Data ===")
|
|
|
|
// Use FDA agent for regulatory assertions
|
|
fdaSigner := registry.signers["fda:drug-label-ingestor"]
|
|
fdaClient := steme.NewClient(baseURL, fdaSigner)
|
|
agentID := fdaSigner.PublicKey()
|
|
|
|
// Create assertion about approved indication
|
|
indication := steme.NewAssertion("semaglutide", "approved_indication").
|
|
WithText("Type 2 diabetes mellitus").
|
|
WithConfidence(1.0).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassRegulatory).
|
|
WithSourceHash("1001111111111111111111111111111111111111111111111111111111111001").
|
|
Build()
|
|
|
|
hash, err := fdaClient.Assert(ctx, indication)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create indication assertion: %v", err)
|
|
} else {
|
|
fmt.Printf(" fda:drug-label-ingestor (T0): %s (indication)\n", hash[:16]+"...")
|
|
}
|
|
|
|
// Make queries to populate audit logs
|
|
_, err = fdaClient.Query(ctx, steme.QueryParams{
|
|
Subject: ptr("semaglutide"),
|
|
Predicate: ptr("approved_indication"),
|
|
Lens: lensPtr(steme.LensAuthority),
|
|
})
|
|
if err != nil {
|
|
log.Printf("Warning: Query failed: %v", err)
|
|
} else {
|
|
fmt.Printf(" Query logged for fda:drug-label-ingestor: %s...\n", agentID[:16])
|
|
}
|
|
|
|
// Make a skeptic query from the internal reviewer
|
|
internalSigner := registry.signers["internal:clinical-ops-reviewer"]
|
|
internalClient := steme.NewClient(baseURL, internalSigner)
|
|
|
|
_, err = internalClient.Skeptic(ctx, steme.SkepticQueryParams{
|
|
Subject: "semaglutide:gastroparesis_risk",
|
|
Predicate: "risk_level",
|
|
})
|
|
if err != nil {
|
|
log.Printf("Warning: Skeptic query failed: %v", err)
|
|
} else {
|
|
internalID := internalSigner.PublicKey()
|
|
fmt.Printf(" Skeptic query logged for internal:clinical-ops-reviewer: %s...\n", internalID[:16])
|
|
}
|
|
|
|
fmt.Println()
|
|
return agentID
|
|
}
|
|
|
|
// populateDemo4TimeDecay creates assertions with different timestamps.
|
|
func populateDemo4TimeDecay(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) {
|
|
fmt.Println("=== Demo 4: Populating Time-Based Assertions ===")
|
|
|
|
// Use FDA agent for market status
|
|
fdaSigner := registry.signers["fda:drug-label-ingestor"]
|
|
client := steme.NewClient(baseURL, fdaSigner)
|
|
|
|
// Current market status
|
|
current := steme.NewAssertion("semaglutide", "market_status").
|
|
WithText("FDA approved for weight management (2024)").
|
|
WithConfidence(1.0).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassRegulatory).
|
|
WithSourceHash("11ae111111111111111111111111111111111111111111111111111111ae1111").
|
|
Build()
|
|
|
|
hash, err := client.Assert(ctx, current)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create current market status: %v", err)
|
|
} else {
|
|
fmt.Printf(" fda:drug-label-ingestor (T0): %s (current status)\n", hash[:16]+"...")
|
|
}
|
|
|
|
// Historical status (deprecated)
|
|
historical := steme.NewAssertion("semaglutide", "market_status").
|
|
WithText("FDA approved for diabetes only (2017)").
|
|
WithConfidence(0.85).
|
|
WithLifecycle(steme.LifecycleDeprecated).
|
|
WithSourceClass(steme.SourceClassRegulatory).
|
|
WithSourceHash("11ae222222222222222222222222222222222222222222222222222222ae2222").
|
|
Build()
|
|
|
|
hash, err = client.Assert(ctx, historical)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to create historical market status: %v", err)
|
|
} else {
|
|
fmt.Printf(" fda:drug-label-ingestor (T0): %s (historical status)\n", hash[:16]+"...")
|
|
}
|
|
|
|
fmt.Println()
|
|
}
|
|
|
|
// populateDemo5TrustSafety creates data that triggers quarantine and circuit breaker demos.
|
|
func populateDemo5TrustSafety(ctx context.Context, _ *AgentRegistry, baseURL string, _ bool) {
|
|
fmt.Println("=== Demo 5: Populating Trust & Safety Triggers ===")
|
|
|
|
// Create multiple untrusted agents for different scenarios
|
|
fmt.Println(" Creating suspicious assertions for quarantine demo...")
|
|
|
|
// Scenario 1: Untrusted high-confidence agent
|
|
untrustedSigner, err := steme.GenerateSigner()
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to generate untrusted signer: %v", err)
|
|
return
|
|
}
|
|
untrustedClient := steme.NewClient(baseURL, untrustedSigner)
|
|
|
|
// This assertion from a new agent with high confidence should trigger quarantine
|
|
suspicious := steme.NewAssertion("miracle_drug", "cures_everything").
|
|
WithBoolean(true).
|
|
WithConfidence(0.99). // Very high confidence from untrusted agent
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassAnecdotal).
|
|
WithSourceHash("5a00111111111111111111111111111111111111111111111111111111005a11").
|
|
Build()
|
|
|
|
hash, err := untrustedClient.Assert(ctx, suspicious)
|
|
if err != nil {
|
|
fmt.Printf(" [Quarantine] High-confidence untrusted: %v\n", err)
|
|
} else {
|
|
fmt.Printf(" [Quarantine] High-confidence untrusted: %s\n", hash[:16]+"...")
|
|
}
|
|
|
|
// Scenario 2: Low-quality content (short, low entropy)
|
|
lowQuality := steme.NewAssertion("test", "x").
|
|
WithText("a"). // Very short, low quality
|
|
WithConfidence(0.5).
|
|
WithLifecycle(steme.LifecycleProposed).
|
|
WithSourceClass(steme.SourceClassAnecdotal).
|
|
WithSourceHash("5a00222222222222222222222222222222222222222222222222222222005a22").
|
|
Build()
|
|
|
|
hash, err = untrustedClient.Assert(ctx, lowQuality)
|
|
if err != nil {
|
|
fmt.Printf(" [Quarantine] Low-quality content: %v\n", err)
|
|
} else {
|
|
fmt.Printf(" [Quarantine] Low-quality content: %s\n", hash[:16]+"...")
|
|
}
|
|
|
|
// Scenario 3: Pattern match (spam-like keywords)
|
|
spammy := steme.NewAssertion("free_bitcoin", "get_rich_quick").
|
|
WithText("Click here for free money! Limited time offer!").
|
|
WithConfidence(0.95).
|
|
WithLifecycle(steme.LifecycleApproved).
|
|
WithSourceClass(steme.SourceClassAnecdotal).
|
|
WithSourceHash("5a00333333333333333333333333333333333333333333333333333333005a33").
|
|
Build()
|
|
|
|
hash, err = untrustedClient.Assert(ctx, spammy)
|
|
if err != nil {
|
|
fmt.Printf(" [Quarantine] Spam pattern: %v\n", err)
|
|
} else {
|
|
fmt.Printf(" [Quarantine] Spam pattern: %s\n", hash[:16]+"...")
|
|
}
|
|
|
|
fmt.Printf(" Untrusted agent ID: %s...\n", untrustedSigner.PublicKey()[:16])
|
|
|
|
// Trigger circuit breaker by sending malformed requests
|
|
fmt.Println("\n Triggering circuit breaker with invalid requests...")
|
|
triggerCircuitBreaker(baseURL)
|
|
|
|
fmt.Println()
|
|
}
|
|
|
|
// triggerCircuitBreaker sends malformed requests to trip the circuit breaker.
|
|
func triggerCircuitBreaker(baseURL string) {
|
|
// Send multiple invalid assertion requests to trigger failures
|
|
for i := 0; i < 5; i++ {
|
|
req, err := http.NewRequest("POST", baseURL+"/v1/assert",
|
|
bytes.NewReader([]byte(`{"invalid": "payload", "missing_required_fields": true}`)))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
// Add a fake agent ID header (invalid signature will cause failure)
|
|
req.Header.Set("X-Agent-Id", fmt.Sprintf("bad_agent_%d", i))
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
fmt.Printf(" [Circuit] Request %d failed: %v\n", i+1, err)
|
|
continue
|
|
}
|
|
resp.Body.Close()
|
|
fmt.Printf(" [Circuit] Request %d: HTTP %d\n", i+1, resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
// ptr returns a pointer to a string.
|
|
func ptr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
// lensPtr returns a pointer to a Lens.
|
|
func lensPtr(l steme.Lens) *steme.Lens {
|
|
return &l
|
|
}
|
|
|
|
// printDemo1Commands prints curl commands for Demo 1.
|
|
func printDemo1Commands(_ Demo1Hashes) {
|
|
fmt.Println("--- Demo 1: Contradictions Visible ---")
|
|
fmt.Println()
|
|
fmt.Println("# Skeptic: See all competing claims")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/skeptic?subject=semaglutide:gastroparesis_risk&predicate=risk_level" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Layered: See per-tier consensus")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/layered?subject=semaglutide:gastroparesis_risk&predicate=risk_level" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Expected: conflict_score > 0.5")
|
|
fmt.Println("# fda:drug-label-ingestor (T0) wins overall, but reddit:health-discussion-scraper (T5) disagrees")
|
|
fmt.Println()
|
|
}
|
|
|
|
// printDemo2Commands prints curl commands for Demo 2.
|
|
func printDemo2Commands(sourceHash string) {
|
|
fmt.Println("--- Demo 2: Cascade Invalidation ---")
|
|
fmt.Println()
|
|
fmt.Println("# Query assertions citing the NEJM source")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/query?subject=GLP1_Agonists" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Expected: Two assertions from pubmed:abstract-indexer (cardiovascular_benefit, mortality_reduction)")
|
|
fmt.Printf("# Both cite source_hash: %s\n", sourceHash)
|
|
fmt.Println()
|
|
}
|
|
|
|
// printDemo3Commands prints curl commands for Demo 3.
|
|
func printDemo3Commands(agentID string) {
|
|
now := time.Now().Unix()
|
|
from := now - 3600 // 1 hour ago
|
|
|
|
fmt.Println("--- Demo 3: Full Audit Trail ---")
|
|
fmt.Println()
|
|
fmt.Println("# List recent query audits")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/audit/queries?limit=10" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Trace fda:drug-label-ingestor decisions")
|
|
fmt.Printf("curl -s \"http://localhost:18180/v1/trace?agent_id=%s&from=%d&limit=50\" | jq\n", agentID, from)
|
|
fmt.Println()
|
|
fmt.Println("# Filter trace by subject pattern")
|
|
fmt.Printf("curl -s \"http://localhost:18180/v1/trace?agent_id=%s&from=%d&subject=semaglutide*\" | jq\n", agentID, from)
|
|
fmt.Println()
|
|
}
|
|
|
|
// printDemo4Commands prints curl commands for Demo 4.
|
|
func printDemo4Commands() {
|
|
now := time.Now().Unix()
|
|
sixMonthsAgo := now - (180 * 24 * 60 * 60)
|
|
|
|
fmt.Println("--- Demo 4: Time Decay ---")
|
|
fmt.Println()
|
|
fmt.Println("# Current state with Recency lens")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/query?subject=semaglutide&predicate=market_status&lens=Recency" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Historical state (point-in-time query)")
|
|
fmt.Printf("curl -s \"http://localhost:18180/v1/query?subject=semaglutide&predicate=market_status&lens=Recency&as_of=%d\" | jq\n", sixMonthsAgo)
|
|
fmt.Println()
|
|
}
|
|
|
|
// printDemo5Commands prints curl commands for Demo 5.
|
|
func printDemo5Commands() {
|
|
fmt.Println("--- Demo 5: Trust & Safety ---")
|
|
fmt.Println()
|
|
fmt.Println("# Check quarantine queue")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/admin/quarantine?limit=10" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# Check tripped circuit breakers")
|
|
fmt.Println(`curl -s "http://localhost:18180/v1/admin/circuit-breakers/tripped" | jq`)
|
|
fmt.Println()
|
|
fmt.Println("# To approve a quarantined assertion:")
|
|
fmt.Println("# curl -X POST \"http://localhost:18180/v1/admin/quarantine/{hash}/approve\"")
|
|
fmt.Println()
|
|
}
|
|
|
|
// sourceClassTier maps SourceClass to tier number.
|
|
var sourceClassTier = map[steme.SourceClass]int{
|
|
steme.SourceClassRegulatory: 0,
|
|
steme.SourceClassClinical: 1,
|
|
steme.SourceClassObservational: 2,
|
|
steme.SourceClassExpert: 3,
|
|
steme.SourceClassCommunity: 4,
|
|
steme.SourceClassAnecdotal: 5,
|
|
}
|
|
|
|
// registerSource registers a source in the source registry.
|
|
// Uses raw HTTP since steme.Client.doJSON is private.
|
|
func registerSource(ctx context.Context, baseURL, name string, src Source) error {
|
|
tier := sourceClassTier[src.Class]
|
|
payload := map[string]interface{}{
|
|
"hash": src.Hash(),
|
|
"label": src.Description,
|
|
"tier": tier,
|
|
"tier_label": string(src.Class),
|
|
"url": src.URL,
|
|
"notes": fmt.Sprintf("Demo source: %s (%s)", name, src.ID),
|
|
}
|
|
|
|
jsonBytes, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal: %w", err)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", baseURL+"/v1/sources", bytes.NewReader(jsonBytes))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// 201 Created = success, 409 Conflict = already exists (OK)
|
|
if resp.StatusCode == 201 || resp.StatusCode == 409 {
|
|
return nil
|
|
}
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
|
}
|