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