OverviewFilesCommitsIssuesDocsReleasesPeers

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

201 lines · scm ref:
;
; continuous-query.scm - Fast query interface for continuous learner
;
; Reads shared state, queues messages for learning
; Non-blocking queries while daemon learns in background
;

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

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

;; ═══════════════════════════════════════════════════════════════════
;; STATE LOADING
;; ═══════════════════════════════════════════════════════════════════

(define *state-file* "data/continuous-state.scm")

(define (load-state!)
  "Load learner state from disk"
  (when (file-exists? *state-file*)
    (primitive-load *state-file*)))

;; ═══════════════════════════════════════════════════════════════════
;; QUEUE
;; ═══════════════════════════════════════════════════════════════════

(define *queue-dir* "data/queue")
(define *queue-file* "data/queue/pending.txt")

(define (ensure-queue-dir)
  (unless (file-exists? "data")
    (mkdir "data"))
  (unless (file-exists? *queue-dir*)
    (mkdir *queue-dir*)))

(define (queue-message! text)
  "Add message to learning queue"
  (ensure-queue-dir)
  (let ((port (open-file *queue-file* "a")))
    (display text port)
    (newline port)
    (close-port port)))

(define (queue-length)
  (if (file-exists? *queue-file*)
      (let ((port (open-input-file *queue-file*)))
        (let loop ((count 0))
          (let ((line (read-line port)))
            (if (eof-object? line)
                (begin (close-port port) count)
                (loop (+ 1 count))))))
      0))

;; ═══════════════════════════════════════════════════════════════════
;; QUERY HANDLER
;; ═══════════════════════════════════════════════════════════════════

(define (handle-query input)
  "Handle a query command"
  (cond
    ;; Help
    ((string=? input "/help")
     "Continuous Learning Commands:
  /stats       - Show learning statistics
  /unit X      - Show everything about unit X
  /assoc X     - Show associations for X
  /similar X   - Show units similar to X
  /grounded    - Show grounding status
  /gaps        - Show knowledge gaps
  /gaps N      - Show top N gaps
  /questions   - Generate questions from gaps
  /vagueness X - Show vagueness for X
  /reload      - Reload state from disk
  /queue       - Show queue status
  <text>       - Queue text for learning")

    ;; Stats
    ((string=? input "/stats")
     (show-stats))

    ;; Unit info
    ((string-prefix? "/unit " input)
     (let ((id (substring input 6)))
       (if (> (get-unit-count id) 0)
           (show-unit id)
           (format #f "Unknown unit: ~a" id))))

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

    ;; Similar units
    ((string-prefix? "/similar " input)
     (let* ((id (substring input 9))
            (similar (find-similar id 0.1 10)))
       (if (null? similar)
           (format #f "No similar units for '~a'" id)
           (string-join
             (cons (format #f "Units similar to '~a':" id)
                   (map (lambda (s)
                          (format #f "  ~a (similarity: ~,2f)" (cdr s) (car s)))
                        similar))
             "\n"))))

    ;; Grounding status
    ((string=? input "/grounded")
     (show-grounded))

    ;; 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")))

    ;; Questions
    ((string=? input "/questions")
     (let ((gaps (detect-gaps)))
       (if (null? gaps)
           "No questions - no gaps detected"
           (string-join
             (cons "Questions to fill gaps:"
                   (map (lambda (i g)
                          (format #f "~a. ~a" (+ 1 i) (gap-to-question g)))
                        (iota (min 10 (length gaps)))
                        (take gaps (min 10 (length gaps)))))
             "\n"))))

    ;; Vagueness
    ((string-prefix? "/vagueness " input)
     (let ((id (substring input 11)))
       (format #f "Vagueness of '~a': ~,3f" id (unit-vagueness id))))

    ((string=? input "/vagueness")
     (format #f "Overall vagueness: ~,3f" (overall-vagueness)))

    ;; Reload
    ((string=? input "/reload")
     (load-state!)
     (format #f "Reloaded: ~a units, ~a pairs"
             (length *unit-counts*) (length *pair-counts*)))

    ;; Queue
    ((string=? input "/queue")
     (format #f "Queue: ~a pending" (queue-length)))

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

;; ═══════════════════════════════════════════════════════════════════
;; REPL
;; ═══════════════════════════════════════════════════════════════════

(define (query-repl)
  "Interactive REPL"
  (load-state!)
  (display (format #f "Loaded: ~a units, ~a pairs\n"
                   (length *unit-counts*) (length *pair-counts*)))
  (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"))

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

(when (defined? 'command-line)
  (let ((args (command-line)))
    (cond
      ((and (> (length args) 1) (string=? (cadr args) "repl"))
       (query-repl))
      ((> (length args) 1)
       ;; Single query mode
       (load-state!)
       (display (handle-query (string-join (cdr args) " ")))
       (newline)))))