OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / opencog-kb / language-learning / chatbot / test-tool-reasoning.scm

256 lines · scm ref:
;
; test-tool-reasoning.scm - Test Layer 8: Inference rule discovery and verification
;
; Tests that the system genuinely "understands" grep:
;   Test 1: Rule discovery - did discover-all-rules! produce ImplicationLinks?
;   Test 2: Preconditions - grep has preconditions "pattern" and "directory"
;   Test 3: Postconditions - grep has postcondition "lines"
;   Test 4: Chaining - grep chains to something (output PMI-matches another tool's input)
;   Test 5: Applicability - grep is applicable for at least one vague word
;   Test 6: Verification - verify-all-rules returns 0 failures
;   Test 7: Layer 8 - inference layer emerged
;
; Run: guile test-tool-reasoning.scm test
;

(use-modules (opencog))
(use-modules (ice-9 rdelim))
(use-modules (ice-9 format))
(use-modules (srfi srfi-1))

(primitive-load "atomspace-learner.scm")

;; ═══════════════════════════════════════════════════════════════════
;; BOOTSTRAP - Load knowledge and discover structures + rules
;; ═══════════════════════════════════════════════════════════════════

(define (load-knowledge filename)
  (display (format #f "Loading ~a...\n" filename))
  (let ((port (open-input-file filename))
        (count 0))
    (let loop ((line (read-line port)))
      (if (eof-object? line)
          (begin (close-port port)
                 (display (format #f "  Processed ~a lines\n" count)))
          (begin
            (let ((trimmed (string-trim-both line)))
              (unless (or (string-null? trimmed)
                          (string-prefix? "#" trimmed))
                (process-text-manual! trimmed)
                (set! count (+ count 1))))
            (loop (read-line port)))))))

;; ═══════════════════════════════════════════════════════════════════
;; LAYER 8 DETECTION (local copy for test - matches daemon logic)
;; ═══════════════════════════════════════════════════════════════════

(define (compute-layer-status-test)
  "Compute all 8 layers for test verification"
  (let* ((words (length (get-all-words)))
         (pairs (length (cog-get-atoms 'EdgeLink #t)))
         (evals (length (cog-get-atoms 'EvaluationLink #t)))
         (impls (length (cog-get-atoms 'ImplicationLink #t)))
         ;; Layer 1: Counting
         (l1 (and (> words 0) (> pairs 0)))
         ;; Layer 2: Association
         (l2 (and l1
                  (> (get-unit-count "atomspace") 0)
                  (let ((a (get-associations "atomspace" 3)))
                    (and (not (null? a)) (> (car (car a)) 0.5)))))
         ;; Layer 3: Structure
         (l3 (and l2 (> evals 0)))
         ;; Layer 4: Response
         (l4 (and l2 (> (get-unit-count "atomspace") 0)))
         ;; Layer 5: Self-awareness
         (l5 (and l4
                  (> (get-unit-count "grep") 0)
                  (> (get-unit-count "wirecore") 0)))
         ;; Layer 6: Tool reasoning
         (l6 (and l5
                  (> (get-unit-count "keypair") 0)
                  (find-tool-for-action "keypair")
                  (find-search-location "keypair")))
         ;; Layer 7: Autonomous action (skip in non-autonomous test)
         (l7 #f)
         ;; Layer 8: Inference - at least 1 rule exists AND all pass verification
         (l8 (and l3
                  (> impls 0)
                  (let ((v (verify-all-rules)))
                    (= (cadr v) 0)))))
    (list
      (cons 1 l1) (cons 2 l2) (cons 3 l3) (cons 4 l4)
      (cons 5 l5) (cons 6 l6) (cons 7 l7) (cons 8 l8))))

;; ═══════════════════════════════════════════════════════════════════
;; TESTS
;; ═══════════════════════════════════════════════════════════════════

(define (test-1-rule-discovery)
  "Test 1: Rule discovery - did discover-all-rules! produce ImplicationLinks?"
  (let ((impls (length (cog-get-atoms 'ImplicationLink #t)))
        (rules (get-all-rules)))
    (display (format #f "  ImplicationLinks: ~a\n" impls))
    (display (format #f "  Rules found: ~a\n" (length rules)))
    (when (not (null? rules))
      (for-each
        (lambda (r)
          (display (format #f "    [~a] ~a(~a, ~a) => ~a(~a, ~a)  [~,2f]\n"
                           (car r)
                           (list-ref r 1) (list-ref r 2) (list-ref r 3)
                           (list-ref r 4) (list-ref r 5) (list-ref r 6)
                           (list-ref r 7))))
        rules))
    (> impls 0)))

(define (test-2-preconditions)
  "Test 2: Preconditions - grep has preconditions 'pattern' and 'directory'"
  (let ((preconds (query-preconditions "grep")))
    (display (format #f "  grep preconditions: ~a\n" preconds))
    (let ((has-pattern (member "pattern" preconds))
          (has-directory (member "directory" preconds)))
      (display (format #f "    pattern: ~a\n" (if has-pattern "YES" "NO")))
      (display (format #f "    directory: ~a\n" (if has-directory "YES" "NO")))
      (and has-pattern has-directory))))

(define (test-3-postconditions)
  "Test 3: Postconditions - grep has postcondition 'lines'"
  (let ((postconds (query-postconditions "grep")))
    (display (format #f "  grep postconditions: ~a\n" postconds))
    (let ((has-lines (member "lines" postconds)))
      (display (format #f "    lines: ~a\n" (if has-lines "YES" "NO")))
      ;; Pass if grep has any postcondition (may also find others)
      (not (null? postconds)))))

(define (test-4-chaining)
  "Test 4: Chaining - grep chains to something"
  (let ((chains (query-chains-to "grep")))
    (display (format #f "  grep chains to: ~a\n" chains))
    ;; Pass if grep can chain to at least one other tool/process
    (not (null? chains))))

(define (test-5-applicability)
  "Test 5: Applicability - grep is applicable for at least one vague word"
  (let ((app-rules (query-rules-by-type "applicability")))
    (display (format #f "  Applicability rules: ~a\n" (length app-rules)))
    (let ((grep-applicable (filter
                             (lambda (r)
                               (string=? (list-ref r 5) "grep"))
                             app-rules)))
      (display (format #f "  grep applicable for: ~a\n"
                       (map (lambda (r) (list-ref r 6)) grep-applicable)))
      (not (null? grep-applicable)))))

(define (test-6-verification)
  "Test 6: Verification - verify-all-rules returns 0 failures"
  (let* ((v (verify-all-rules))
         (passes (car v))
         (fails (cadr v))
         (failures (caddr v)))
    (display (format #f "  Passes: ~a  Fails: ~a\n" passes fails))
    (when (not (null? failures))
      (for-each
        (lambda (f) (display (format #f "    FAIL: ~a\n" f)))
        failures))
    (= fails 0)))

(define (test-7-layer-8)
  "Test 7: Layer 8 - inference layer emerged"
  (let ((layers (compute-layer-status-test)))
    (for-each
      (lambda (l)
        (let ((names '((1 . "Counting") (2 . "Association") (3 . "Structure")
                       (4 . "Response") (5 . "Self-awareness") (6 . "Tool reasoning")
                       (7 . "Autonomous action") (8 . "Inference"))))
          (display (format #f "    Layer ~a (~a): ~a\n"
                           (car l)
                           (cdr (assoc (car l) names))
                           (if (cdr l) "EMERGED" "not yet")))))
      layers)
    ;; Check Layer 8 specifically
    (let ((l8 (assoc 8 layers)))
      (and l8 (cdr l8)))))

;; ═══════════════════════════════════════════════════════════════════
;; MAIN TEST
;; ═══════════════════════════════════════════════════════════════════

(define (run-tool-reasoning-test)
  (display "\n")
  (display "════════════════════════════════════════════════════════════\n")
  (display "  TOOL REASONING TEST: Layer 8 - Inference Rules\n")
  (display "════════════════════════════════════════════════════════════\n\n")

  ;; Phase 1: Bootstrap — load manual knowledge (including unix tools for grounding)
  (display "PHASE 1: Loading bootstrap knowledge\n")
  (load-knowledge "data/self-knowledge.txt")
  (load-knowledge "data/anproto-knowledge.txt")
  (load-knowledge "data/unix-tools.txt")
  (display (format #f "Bootstrap: ~a manual observations, ~a words\n\n"
                   *manual-observations* (length (get-all-words))))

  ;; Phase 2: Discover structures from PMI
  (display "PHASE 2: Discovering structures\n")
  (discover-all-structures!)
  (display (format #f "EvaluationLinks: ~a\n\n" (length (cog-get-atoms 'EvaluationLink #t))))

  ;; Phase 3: Infer rules from structures
  (display "PHASE 3: Inferring rules from structures\n")
  (discover-all-rules!)
  (display (format #f "\n~a\n" (show-stats)))

  ;; Run tests
  (let ((results '()))
    (define (run-test n name test-fn)
      (display (format #f "\n── Test ~a: ~a ──\n" n name))
      (let ((pass (test-fn)))
        (set! results (cons (list n name pass) results))
        (display (format #f "  Result: ~a\n" (if pass "PASS" "FAIL")))))

    (run-test 1 "Rule discovery" test-1-rule-discovery)
    (run-test 2 "Preconditions" test-2-preconditions)
    (run-test 3 "Postconditions" test-3-postconditions)
    (run-test 4 "Chaining" test-4-chaining)
    (run-test 5 "Applicability" test-5-applicability)
    (run-test 6 "Verification" test-6-verification)
    (run-test 7 "Layer 8 emergence" test-7-layer-8)

    ;; Display all rules
    (display "\n── All discovered rules ──\n")
    (display (show-rules))
    (display "\n")

    ;; Summary
    (display "\n════════════════════════════════════════════════════════════\n")
    (display "  TEST SUMMARY\n")
    (display "════════════════════════════════════════════════════════════\n\n")

    (let ((passed 0) (failed 0))
      (for-each
        (lambda (r)
          (display (format #f "  Test ~a (~a): ~a\n"
                           (car r) (cadr r)
                           (if (caddr r) "PASS" "FAIL")))
          (if (caddr r)
              (set! passed (+ passed 1))
              (set! failed (+ failed 1))))
        (reverse results))

      (display (format #f "\n  ~a/~a tests passed\n" passed (+ passed failed)))
      (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))

      (display "\n  AtomSpace:\n")
      (display (format #f "    ConceptNodes:      ~a\n" (length (cog-get-atoms 'ConceptNode #t))))
      (display (format #f "    EdgeLinks:         ~a\n" (length (cog-get-atoms 'EdgeLink #t))))
      (display (format #f "    EvaluationLinks:   ~a\n" (length (cog-get-atoms 'EvaluationLink #t))))
      (display (format #f "    ImplicationLinks:  ~a\n" (length (cog-get-atoms 'ImplicationLink #t))))
      (display (format #f "    Total words:       ~a\n" (length (get-all-words))))

      (display "\n════════════════════════════════════════════════════════════\n")
      (display "  END\n")
      (display "════════════════════════════════════════════════════════════\n"))))

(when (and (defined? 'command-line)
           (> (length (command-line)) 1)
           (string=? (cadr (command-line)) "test"))
  (run-tool-reasoning-test))