OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / opencog-kb / language-learning / chatbot / query.scm

221 lines · scm ref:
;
; query.scm - Fast query interface
;
; Lightweight process for instant queries.
; Queues messages for background learning, reads current state.
;

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

;; Load dependencies using primitive-load (works with relative paths from cwd)
(primitive-load "layer1.scm")
(primitive-load "layer2.scm")
(primitive-load "layer3.scm")
(primitive-load "vagueness.scm")
(primitive-load "autodiscover.scm")
(primitive-load "persist.scm")
(primitive-load "queue.scm")
(primitive-load "gap-detector.scm")

;; ═══════════════════════════════════════════════════════════════════
;; QUERY COMMANDS
;; ═══════════════════════════════════════════════════════════════════

(define (handle-query input)
  "Handle a query and return response"
  (cond
    ;; Help
    ((string=? input "/help")
     "Commands:
  /stats      - Show knowledge statistics
  /words X    - Show word associations for X
  /cluster X  - Show cluster info for X
  /clusters   - List all clusters
  /relations  - Show cluster relations
  /vagueness  - Show uncertainty metrics
  /gaps       - Show knowledge gaps (what we don't know)
  /gaps N     - Show top N gaps
  /questions  - Generate questions from gaps
  /queue      - Show queue status
  /reload     - Reload knowledge from disk
  <text>      - Queue text for learning")

    ;; Stats
    ((string=? input "/stats")
     (format #f "Words: ~a
Pairs: ~a
Clusters: ~a
Relations: ~a
~a
~a"
             (layer1-word-count)
             (layer1-pair-count)
             (layer2-cluster-count)
             (layer3-relation-count)
             (show-autodiscover-stats)
             (queue-stats)))

    ;; Word associations
    ((string-prefix? "/words " input)
     (let ((word (substring input 7)))
       (let ((assocs (layer1-associations word 10)))
         (if (null? assocs)
             (format #f "No associations for '~a'" word)
             (string-join
               (cons (format #f "Associations for '~a':" word)
                     (map (lambda (a)
                            (format #f "  ~a (PMI: ~,2f)" (cdr a) (car a)))
                          assocs))
               "\n")))))

    ;; Cluster info
    ((string-prefix? "/cluster " input)
     (let ((word (substring input 9)))
       (show-cluster word)))

    ;; List clusters
    ((string=? input "/clusters")
     (show-clusters))

    ;; Relations
    ((string-prefix? "/relations " input)
     (let ((cluster (substring input 11)))
       (show-relations cluster)))

    ((string=? input "/relations")
     (show-all-relations))

    ;; Vagueness
    ((string-prefix? "/vagueness " input)
     (let ((word (substring input 11)))
       (show-word-vagueness word)))

    ((string=? input "/vagueness")
     (show-overall-vagueness))

    ;; Queue status
    ((string=? input "/queue")
     (queue-stats))

    ;; Reload knowledge
    ((string=? input "/reload")
     (load-knowledge)
     (format #f "Reloaded: ~a words, ~a pairs"
             (layer1-word-count) (layer1-pair-count)))

    ;; Autodiscover
    ((string=? input "/auto-stats")
     (show-autodiscover-stats))

    ((string-prefix? "/auto-level " input)
     (let ((level (string->number (substring input 12))))
       (if level
           (show-level level)
           "Invalid level number")))

    ;; Knowledge gaps
    ((string=? input "/gaps")
     (show-gaps 10))

    ((string-prefix? "/gaps " input)
     (let ((n (string->number (substring input 6))))
       (if n
           (show-gaps n)
           "Invalid number")))

    ((string=? input "/questions")
     (show-questions 10))

    ((string-prefix? "/questions " input)
     (let ((n (string->number (substring input 11))))
       (if n
           (show-questions n)
           "Invalid number")))

    ;; Default: queue for learning
    (else
     (queue-message input)
     (format #f "Queued for learning (~a pending)" (queue-length)))))

;; ═══════════════════════════════════════════════════════════════════
;; REPL INTERFACE
;; ═══════════════════════════════════════════════════════════════════

(define (query-repl)
  "Interactive query REPL"
  ;; Load current knowledge
  (load-knowledge)
  (display (format #f "Loaded: ~a words, ~a pairs~%"
                   (layer1-word-count) (layer1-pair-count)))
  (display "Type /help for commands, Ctrl+D to exit\n\n")

  (let loop ()
    (display "> ")
    (let ((input (read-line)))
      (unless (eof-object? input)
        (let ((trimmed (string-trim-both input)))
          (unless (string-null? trimmed)
            (display (handle-query trimmed))
            (newline)))
        (loop))))

  (display "\nGoodbye!\n"))

;; ═══════════════════════════════════════════════════════════════════
;; SOCKET SERVER (for programmatic access)
;; ═══════════════════════════════════════════════════════════════════

(define (query-server socket-path)
  "Run query server on Unix socket"
  (use-modules (ice-9 threads))

  ;; Load knowledge once at startup
  (load-knowledge)
  (display (format #f "[query] Loaded: ~a words, ~a pairs~%"
                   (layer1-word-count) (layer1-pair-count)))

  ;; Remove old socket
  (when (file-exists? socket-path)
    (delete-file socket-path))

  ;; Create socket
  (let ((sock (socket PF_UNIX SOCK_STREAM 0)))
    (bind sock AF_UNIX socket-path)
    (listen sock 5)
    (display (format #f "[query] Listening on ~a~%" socket-path))

    ;; Accept loop
    (while #t
      (let* ((client-info (accept sock))
             (client (car client-info)))
        ;; Handle in thread for non-blocking
        (call-with-new-thread
          (lambda ()
            (let ((input (read-line client)))
              (unless (eof-object? input)
                (let ((response (handle-query (string-trim-both input))))
                  (display response client)
                  (newline client))))
            (close client)))))))

;; ═══════════════════════════════════════════════════════════════════
;; ENTRY POINT
;; ═══════════════════════════════════════════════════════════════════

(when (defined? 'command-line)
  (let ((args (command-line)))
    (cond
      ((and (> (length args) 1) (string=? (cadr args) "repl"))
       (query-repl))
      ((and (> (length args) 1) (string=? (cadr args) "server"))
       (query-server "/tmp/chatbot-query.sock"))
      ((> (length args) 1)
       ;; Single query mode
       (load-knowledge)
       (display (handle-query (string-join (cdr args) " ")))
       (newline)))))