cli-memory-os 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_memory_os-0.1.2/cli_memory_os.egg-info → cli_memory_os-0.1.3}/PKG-INFO +1 -1
- cli_memory_os-0.1.3/cli/commands/ask.py +67 -0
- cli_memory_os-0.1.3/cli/commands/sync.py +70 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/main.py +14 -29
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3/cli_memory_os.egg-info}/PKG-INFO +1 -1
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/SOURCES.txt +3 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/pyproject.toml +1 -1
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_first_time_user_experience.py +1 -0
- cli_memory_os-0.1.3/tests/test_sync_ask_cli.py +111 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/LICENSE +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/MANIFEST.in +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/README.md +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/benchmark.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/config_cmd.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/doctor.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/export.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/import_cmd.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/init.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/logs.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/monitor.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/plugins.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/restart.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/start.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/status.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/stop.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/version.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/commands/workspace.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli/parser.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/dependency_links.txt +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/entry_points.txt +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/requires.txt +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/top_level.txt +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/base.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/github.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/gmail.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/notion.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/connectors/registry.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/chunker.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/context_builder.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/embedder.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/llm.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/core/vector_store.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/compose.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/config.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/docker.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/health.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/observability.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/infrastructure/workspace.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/models/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/models/memory.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/setup.cfg +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/storage/__init__.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/storage/db.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/storage/graph.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/storage/schema.sql +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/storage/tech_detector.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_cli_integration.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_commands_phase2.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_compose_manager.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_infrastructure_stabilization.py +0 -0
- {cli_memory_os-0.1.2 → cli_memory_os-0.1.3}/tests/test_v1_features.py +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command: memory-os ask
|
|
3
|
+
|
|
4
|
+
Queries the RAG pipeline with a question.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import sqlite3
|
|
9
|
+
from core.llm import run_hybrid_rag
|
|
10
|
+
from storage.db import init_db
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_and_print_ask(query: str):
|
|
14
|
+
"""Run RAG query pipeline and print the structured result blocks."""
|
|
15
|
+
rag_res = run_hybrid_rag(query)
|
|
16
|
+
|
|
17
|
+
print("========================================")
|
|
18
|
+
print("ANSWER")
|
|
19
|
+
print("========================================")
|
|
20
|
+
print(rag_res["answer"])
|
|
21
|
+
print("\n========================================")
|
|
22
|
+
print("SOURCES")
|
|
23
|
+
print("========================================")
|
|
24
|
+
if rag_res.get("sources"):
|
|
25
|
+
for s in rag_res["sources"]:
|
|
26
|
+
print(f"- {s}")
|
|
27
|
+
else:
|
|
28
|
+
print("None")
|
|
29
|
+
print("\n========================================")
|
|
30
|
+
print("REPOSITORIES USED")
|
|
31
|
+
print("========================================")
|
|
32
|
+
if rag_res.get("repositories"):
|
|
33
|
+
for r in rag_res["repositories"]:
|
|
34
|
+
print(f"- {r}")
|
|
35
|
+
else:
|
|
36
|
+
print("None")
|
|
37
|
+
print("\n========================================")
|
|
38
|
+
confidence = rag_res.get("confidence", 0.0)
|
|
39
|
+
print(f"Confidence: {confidence:.2f}")
|
|
40
|
+
print("========================================")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def execute(args):
|
|
44
|
+
"""Run the ask command parser handler."""
|
|
45
|
+
# Ensure database is initialized
|
|
46
|
+
try:
|
|
47
|
+
init_db()
|
|
48
|
+
except sqlite3.OperationalError as e:
|
|
49
|
+
print("----------------------------------")
|
|
50
|
+
print("Workspace has not been initialized.")
|
|
51
|
+
print("")
|
|
52
|
+
print("Run:")
|
|
53
|
+
print("memory-os init")
|
|
54
|
+
print("----------------------------------")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
if not args.question:
|
|
58
|
+
print("Usage: memory-os ask <question>")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
query = " ".join(args.question)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
run_and_print_ask(query)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f"❌ Error executing query: {e}")
|
|
67
|
+
sys.exit(1)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command: memory-os sync
|
|
3
|
+
|
|
4
|
+
Synchronizes data from connected sources (GitHub, Gmail, Notion),
|
|
5
|
+
chunks documents, generates embeddings, and syncs databases.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import logging
|
|
11
|
+
import sqlite3
|
|
12
|
+
from storage.db import init_db, clear_all
|
|
13
|
+
from core.vector_store import run_reindexing, close_qdrant_client
|
|
14
|
+
from storage.graph import GraphStore
|
|
15
|
+
from connectors.github import sync_github
|
|
16
|
+
from connectors.gmail import sync_gmail
|
|
17
|
+
from connectors.notion import sync_notion
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def execute(args):
|
|
21
|
+
"""Run the synchronization pipeline."""
|
|
22
|
+
# Ensure database is initialized
|
|
23
|
+
try:
|
|
24
|
+
init_db()
|
|
25
|
+
except sqlite3.OperationalError as e:
|
|
26
|
+
print("----------------------------------")
|
|
27
|
+
print("Workspace has not been initialized.")
|
|
28
|
+
print("")
|
|
29
|
+
print("Run:")
|
|
30
|
+
print("memory-os init")
|
|
31
|
+
print("----------------------------------")
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
start_time = time.perf_counter()
|
|
35
|
+
|
|
36
|
+
rebuild = getattr(args, "rebuild", False)
|
|
37
|
+
source = getattr(args, "source", None)
|
|
38
|
+
|
|
39
|
+
if rebuild:
|
|
40
|
+
print("Performing full rebuild reset...")
|
|
41
|
+
clear_all()
|
|
42
|
+
close_qdrant_client()
|
|
43
|
+
run_reindexing()
|
|
44
|
+
GraphStore().clear_graph()
|
|
45
|
+
|
|
46
|
+
# Sync based on source selection
|
|
47
|
+
if source == "github":
|
|
48
|
+
print("Syncing GitHub source...")
|
|
49
|
+
sync_github()
|
|
50
|
+
elif source == "gmail":
|
|
51
|
+
print("Syncing Gmail source...")
|
|
52
|
+
sync_gmail()
|
|
53
|
+
elif source == "notion":
|
|
54
|
+
print("Syncing Notion source...")
|
|
55
|
+
sync_notion()
|
|
56
|
+
else:
|
|
57
|
+
print("Syncing all sources (GitHub, Gmail, Notion)...")
|
|
58
|
+
sync_github()
|
|
59
|
+
sync_gmail()
|
|
60
|
+
sync_notion()
|
|
61
|
+
|
|
62
|
+
print("Running document chunking and vector reindexing...")
|
|
63
|
+
run_reindexing()
|
|
64
|
+
|
|
65
|
+
print("Extracting and syncing knowledge graph relationships...")
|
|
66
|
+
GraphStore().extract_and_sync_graph()
|
|
67
|
+
|
|
68
|
+
duration = time.perf_counter() - start_time
|
|
69
|
+
print(f"\nSync complete. Total Duration: {duration:.2f}s")
|
|
70
|
+
logging.getLogger("main").info(f"Sync completed in {duration:.2f}s")
|
|
@@ -115,7 +115,6 @@ def run_interactive():
|
|
|
115
115
|
close_qdrant_client,
|
|
116
116
|
)
|
|
117
117
|
from core.embedder import Embedder
|
|
118
|
-
from core.llm import run_hybrid_rag
|
|
119
118
|
from storage.graph import GraphStore
|
|
120
119
|
from connectors.github import sync_github
|
|
121
120
|
from connectors.gmail import sync_gmail
|
|
@@ -283,30 +282,11 @@ def run_interactive():
|
|
|
283
282
|
if not arg:
|
|
284
283
|
print("Usage: ask <question>")
|
|
285
284
|
else:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
print("\n========================================")
|
|
292
|
-
print("SOURCES")
|
|
293
|
-
print("========================================")
|
|
294
|
-
if rag_res["sources"]:
|
|
295
|
-
for s in rag_res["sources"]:
|
|
296
|
-
print(f"- {s}")
|
|
297
|
-
else:
|
|
298
|
-
print("None")
|
|
299
|
-
print("\n========================================")
|
|
300
|
-
print("REPOSITORIES USED")
|
|
301
|
-
print("========================================")
|
|
302
|
-
if rag_res["repositories"]:
|
|
303
|
-
for r in rag_res["repositories"]:
|
|
304
|
-
print(f"- {r}")
|
|
305
|
-
else:
|
|
306
|
-
print("None")
|
|
307
|
-
print("\n========================================")
|
|
308
|
-
print(f"Confidence: {rag_res['confidence']:.2f}")
|
|
309
|
-
print("========================================")
|
|
285
|
+
from cli.commands.ask import run_and_print_ask
|
|
286
|
+
try:
|
|
287
|
+
run_and_print_ask(arg)
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f"❌ Error executing query: {e}")
|
|
310
290
|
|
|
311
291
|
elif cmd == "repo-info":
|
|
312
292
|
if not arg:
|
|
@@ -441,11 +421,16 @@ def route_command(args):
|
|
|
441
421
|
elif command == "logs":
|
|
442
422
|
from cli.commands.logs import execute
|
|
443
423
|
execute(args)
|
|
424
|
+
elif command == "sync":
|
|
425
|
+
from cli.commands.sync import execute
|
|
426
|
+
execute(args)
|
|
427
|
+
elif command == "ask":
|
|
428
|
+
from cli.commands.ask import execute
|
|
429
|
+
execute(args)
|
|
444
430
|
else:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
print("Run 'memory-os' (no arguments) for the interactive REPL.")
|
|
431
|
+
from cli.parser import build_parser
|
|
432
|
+
build_parser().print_help()
|
|
433
|
+
sys.exit(1)
|
|
449
434
|
|
|
450
435
|
|
|
451
436
|
|
|
@@ -6,6 +6,7 @@ cli/__init__.py
|
|
|
6
6
|
cli/main.py
|
|
7
7
|
cli/parser.py
|
|
8
8
|
cli/commands/__init__.py
|
|
9
|
+
cli/commands/ask.py
|
|
9
10
|
cli/commands/benchmark.py
|
|
10
11
|
cli/commands/config_cmd.py
|
|
11
12
|
cli/commands/doctor.py
|
|
@@ -19,6 +20,7 @@ cli/commands/restart.py
|
|
|
19
20
|
cli/commands/start.py
|
|
20
21
|
cli/commands/status.py
|
|
21
22
|
cli/commands/stop.py
|
|
23
|
+
cli/commands/sync.py
|
|
22
24
|
cli/commands/version.py
|
|
23
25
|
cli/commands/workspace.py
|
|
24
26
|
cli_memory_os.egg-info/PKG-INFO
|
|
@@ -58,4 +60,5 @@ tests/test_commands_phase2.py
|
|
|
58
60
|
tests/test_compose_manager.py
|
|
59
61
|
tests/test_first_time_user_experience.py
|
|
60
62
|
tests/test_infrastructure_stabilization.py
|
|
63
|
+
tests/test_sync_ask_cli.py
|
|
61
64
|
tests/test_v1_features.py
|
|
@@ -76,6 +76,7 @@ def test_init_missing_docker(capsys):
|
|
|
76
76
|
"""Test memory-os init when Docker Desktop is not running."""
|
|
77
77
|
with patch("infrastructure.docker.check_docker_installed", return_value=(True, "Docker version 24.0.7")), \
|
|
78
78
|
patch("infrastructure.docker.check_docker_compose_installed", return_value=(True, "Docker Compose version 2.23.3")), \
|
|
79
|
+
patch("infrastructure.docker.check_docker_daemon", return_value=(False, "Docker Desktop is not running.")), \
|
|
79
80
|
patch("infrastructure.docker.check_docker_running", return_value=False):
|
|
80
81
|
|
|
81
82
|
with pytest.raises(SystemExit) as excinfo:
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit and integration tests for sync, ask commands, and the interactive REPL.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
import unittest
|
|
8
|
+
from unittest.mock import patch, MagicMock
|
|
9
|
+
|
|
10
|
+
# Ensure project root is in path
|
|
11
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DummyArgs:
|
|
15
|
+
def __init__(self, **kwargs):
|
|
16
|
+
for k, v in kwargs.items():
|
|
17
|
+
setattr(self, k, v)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestSyncAskCLI(unittest.TestCase):
|
|
21
|
+
"""Test suite verifying sync and ask CLI command routing and logic."""
|
|
22
|
+
|
|
23
|
+
@patch("cli.commands.sync.init_db")
|
|
24
|
+
@patch("cli.commands.sync.sync_github")
|
|
25
|
+
@patch("cli.commands.sync.sync_gmail")
|
|
26
|
+
@patch("cli.commands.sync.sync_notion")
|
|
27
|
+
@patch("cli.commands.sync.run_reindexing")
|
|
28
|
+
@patch("cli.commands.sync.GraphStore")
|
|
29
|
+
def test_sync_command_all_sources(
|
|
30
|
+
self, mock_graph_store_class, mock_run_reindexing, mock_sync_notion, mock_sync_gmail, mock_sync_github, mock_init_db
|
|
31
|
+
):
|
|
32
|
+
"""Test memory-os sync command runs sync for all sources."""
|
|
33
|
+
mock_graph_inst = MagicMock()
|
|
34
|
+
mock_graph_store_class.return_value = mock_graph_inst
|
|
35
|
+
|
|
36
|
+
from cli.commands.sync import execute as execute_sync
|
|
37
|
+
|
|
38
|
+
args = DummyArgs(rebuild=False, source=None)
|
|
39
|
+
execute_sync(args)
|
|
40
|
+
|
|
41
|
+
mock_init_db.assert_called_once()
|
|
42
|
+
mock_sync_github.assert_called_once()
|
|
43
|
+
mock_sync_gmail.assert_called_once()
|
|
44
|
+
mock_sync_notion.assert_called_once()
|
|
45
|
+
mock_run_reindexing.assert_called_once()
|
|
46
|
+
mock_graph_inst.extract_and_sync_graph.assert_called_once()
|
|
47
|
+
|
|
48
|
+
@patch("cli.commands.sync.init_db")
|
|
49
|
+
@patch("cli.commands.sync.sync_github")
|
|
50
|
+
@patch("cli.commands.sync.run_reindexing")
|
|
51
|
+
@patch("cli.commands.sync.GraphStore")
|
|
52
|
+
def test_sync_command_single_source(
|
|
53
|
+
self, mock_graph_store_class, mock_run_reindexing, mock_sync_github, mock_init_db
|
|
54
|
+
):
|
|
55
|
+
"""Test memory-os sync command can sync only a single source."""
|
|
56
|
+
mock_graph_inst = MagicMock()
|
|
57
|
+
mock_graph_store_class.return_value = mock_graph_inst
|
|
58
|
+
|
|
59
|
+
from cli.commands.sync import execute as execute_sync
|
|
60
|
+
|
|
61
|
+
args = DummyArgs(rebuild=False, source="github")
|
|
62
|
+
execute_sync(args)
|
|
63
|
+
|
|
64
|
+
mock_init_db.assert_called_once()
|
|
65
|
+
mock_sync_github.assert_called_once()
|
|
66
|
+
mock_run_reindexing.assert_called_once()
|
|
67
|
+
mock_graph_inst.extract_and_sync_graph.assert_called_once()
|
|
68
|
+
|
|
69
|
+
@patch("cli.commands.ask.init_db")
|
|
70
|
+
@patch("cli.commands.ask.run_hybrid_rag")
|
|
71
|
+
def test_ask_command(self, mock_run_hybrid_rag, mock_init_db):
|
|
72
|
+
"""Test memory-os ask command query execution."""
|
|
73
|
+
mock_run_hybrid_rag.return_value = {
|
|
74
|
+
"answer": "Python is used in core services.",
|
|
75
|
+
"sources": ["source_1.txt"],
|
|
76
|
+
"repositories": ["repo-a"],
|
|
77
|
+
"confidence": 0.95
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
from cli.commands.ask import execute as execute_ask
|
|
81
|
+
|
|
82
|
+
args = DummyArgs(question=["What", "projects", "use", "Python?"])
|
|
83
|
+
execute_ask(args)
|
|
84
|
+
|
|
85
|
+
mock_init_db.assert_called_once()
|
|
86
|
+
mock_run_hybrid_rag.assert_called_once_with("What projects use Python?")
|
|
87
|
+
|
|
88
|
+
@patch("storage.db.init_db")
|
|
89
|
+
@patch("core.embedder.Embedder")
|
|
90
|
+
@patch("cli.main.input")
|
|
91
|
+
@patch("cli.commands.ask.run_and_print_ask")
|
|
92
|
+
def test_interactive_repl_ask(self, mock_run_and_print_ask, mock_input, mock_embedder_class, mock_init_db):
|
|
93
|
+
"""Test interactive REPL triggers the ask command routing for natural language query."""
|
|
94
|
+
# Mock REPL sequence: 1. A question, 2. 'exit' to terminate loop
|
|
95
|
+
mock_input.side_effect = ["What projects use Python?", "exit"]
|
|
96
|
+
|
|
97
|
+
# Mock embedder instantiation
|
|
98
|
+
mock_embedder_inst = MagicMock()
|
|
99
|
+
mock_embedder_class.return_value = mock_embedder_inst
|
|
100
|
+
|
|
101
|
+
from cli.main import run_interactive
|
|
102
|
+
|
|
103
|
+
with patch("core.vector_store.close_qdrant_client"), patch("storage.graph.GraphStore"):
|
|
104
|
+
run_interactive()
|
|
105
|
+
|
|
106
|
+
# Verify that natural language query gets routed to ask and calls run_and_print_ask
|
|
107
|
+
mock_run_and_print_ask.assert_called_once_with("What projects use Python?")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|