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