gnosisllm-knowledge 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- gnosisllm_knowledge/__init__.py +91 -39
- gnosisllm_knowledge/api/__init__.py +3 -2
- gnosisllm_knowledge/api/knowledge.py +502 -32
- gnosisllm_knowledge/api/memory.py +966 -0
- gnosisllm_knowledge/backends/__init__.py +14 -5
- gnosisllm_knowledge/backends/memory/indexer.py +27 -2
- gnosisllm_knowledge/backends/memory/searcher.py +111 -10
- gnosisllm_knowledge/backends/opensearch/agentic.py +355 -48
- gnosisllm_knowledge/backends/opensearch/config.py +49 -28
- gnosisllm_knowledge/backends/opensearch/indexer.py +49 -3
- gnosisllm_knowledge/backends/opensearch/mappings.py +14 -5
- gnosisllm_knowledge/backends/opensearch/memory/__init__.py +12 -0
- gnosisllm_knowledge/backends/opensearch/memory/client.py +1380 -0
- gnosisllm_knowledge/backends/opensearch/memory/config.py +127 -0
- gnosisllm_knowledge/backends/opensearch/memory/setup.py +322 -0
- gnosisllm_knowledge/backends/opensearch/queries.py +33 -33
- gnosisllm_knowledge/backends/opensearch/searcher.py +238 -0
- gnosisllm_knowledge/backends/opensearch/setup.py +308 -148
- gnosisllm_knowledge/cli/app.py +436 -31
- gnosisllm_knowledge/cli/commands/agentic.py +26 -9
- gnosisllm_knowledge/cli/commands/load.py +169 -19
- gnosisllm_knowledge/cli/commands/memory.py +733 -0
- gnosisllm_knowledge/cli/commands/search.py +9 -10
- gnosisllm_knowledge/cli/commands/setup.py +49 -23
- gnosisllm_knowledge/cli/display/service.py +43 -0
- gnosisllm_knowledge/cli/utils/config.py +62 -4
- gnosisllm_knowledge/core/domain/__init__.py +54 -0
- gnosisllm_knowledge/core/domain/discovery.py +166 -0
- gnosisllm_knowledge/core/domain/document.py +19 -19
- gnosisllm_knowledge/core/domain/memory.py +440 -0
- gnosisllm_knowledge/core/domain/result.py +11 -3
- gnosisllm_knowledge/core/domain/search.py +12 -25
- gnosisllm_knowledge/core/domain/source.py +11 -12
- gnosisllm_knowledge/core/events/__init__.py +8 -0
- gnosisllm_knowledge/core/events/types.py +198 -5
- gnosisllm_knowledge/core/exceptions.py +227 -0
- gnosisllm_knowledge/core/interfaces/__init__.py +17 -0
- gnosisllm_knowledge/core/interfaces/agentic.py +11 -3
- gnosisllm_knowledge/core/interfaces/indexer.py +10 -1
- gnosisllm_knowledge/core/interfaces/memory.py +524 -0
- gnosisllm_knowledge/core/interfaces/searcher.py +10 -1
- gnosisllm_knowledge/core/interfaces/streaming.py +133 -0
- gnosisllm_knowledge/core/streaming/__init__.py +36 -0
- gnosisllm_knowledge/core/streaming/pipeline.py +228 -0
- gnosisllm_knowledge/fetchers/__init__.py +8 -0
- gnosisllm_knowledge/fetchers/config.py +27 -0
- gnosisllm_knowledge/fetchers/neoreader.py +31 -3
- gnosisllm_knowledge/fetchers/neoreader_discovery.py +505 -0
- gnosisllm_knowledge/loaders/__init__.py +5 -1
- gnosisllm_knowledge/loaders/base.py +3 -4
- gnosisllm_knowledge/loaders/discovery.py +338 -0
- gnosisllm_knowledge/loaders/discovery_streaming.py +343 -0
- gnosisllm_knowledge/loaders/factory.py +46 -0
- gnosisllm_knowledge/loaders/sitemap.py +129 -1
- gnosisllm_knowledge/loaders/sitemap_streaming.py +258 -0
- gnosisllm_knowledge/services/indexing.py +100 -93
- gnosisllm_knowledge/services/search.py +84 -31
- gnosisllm_knowledge/services/streaming_pipeline.py +334 -0
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.4.0.dist-info}/METADATA +73 -10
- gnosisllm_knowledge-0.4.0.dist-info/RECORD +81 -0
- gnosisllm_knowledge-0.2.0.dist-info/RECORD +0 -64
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.4.0.dist-info}/WHEEL +0 -0
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.4.0.dist-info}/entry_points.txt +0 -0
gnosisllm_knowledge/cli/app.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""GnosisLLM Knowledge CLI Application.
|
|
2
2
|
|
|
3
3
|
Main entry point assembling all CLI commands with enterprise-grade UX.
|
|
4
|
+
|
|
5
|
+
Note:
|
|
6
|
+
This library is tenant-agnostic. Multi-tenancy is achieved through index
|
|
7
|
+
isolation - each tenant should use a separate index (e.g., "knowledge-{account_id}").
|
|
8
|
+
Use --index to target tenant-specific indices.
|
|
4
9
|
"""
|
|
5
10
|
|
|
6
11
|
from __future__ import annotations
|
|
@@ -63,13 +68,13 @@ def main_callback(
|
|
|
63
68
|
@app.command()
|
|
64
69
|
def setup(
|
|
65
70
|
host: Annotated[
|
|
66
|
-
str,
|
|
67
|
-
typer.Option("--host", "-h", help="OpenSearch host."),
|
|
68
|
-
] =
|
|
71
|
+
Optional[str],
|
|
72
|
+
typer.Option("--host", "-h", help="OpenSearch host (default: from OPENSEARCH_HOST env)."),
|
|
73
|
+
] = None,
|
|
69
74
|
port: Annotated[
|
|
70
|
-
int,
|
|
71
|
-
typer.Option("--port", "-p", help="OpenSearch port."),
|
|
72
|
-
] =
|
|
75
|
+
Optional[int],
|
|
76
|
+
typer.Option("--port", "-p", help="OpenSearch port (default: from OPENSEARCH_PORT env)."),
|
|
77
|
+
] = None,
|
|
73
78
|
username: Annotated[
|
|
74
79
|
Optional[str],
|
|
75
80
|
typer.Option("--username", "-u", help="OpenSearch username."),
|
|
@@ -79,13 +84,13 @@ def setup(
|
|
|
79
84
|
typer.Option("--password", help="OpenSearch password."),
|
|
80
85
|
] = None,
|
|
81
86
|
use_ssl: Annotated[
|
|
82
|
-
bool,
|
|
83
|
-
typer.Option("--use-ssl", help="Enable SSL."),
|
|
84
|
-
] =
|
|
87
|
+
Optional[bool],
|
|
88
|
+
typer.Option("--use-ssl/--no-ssl", help="Enable/disable SSL (default: from OPENSEARCH_USE_SSL env)."),
|
|
89
|
+
] = None,
|
|
85
90
|
verify_certs: Annotated[
|
|
86
|
-
bool,
|
|
87
|
-
typer.Option("--verify-certs", help="Verify SSL certificates."),
|
|
88
|
-
] =
|
|
91
|
+
Optional[bool],
|
|
92
|
+
typer.Option("--verify-certs/--no-verify-certs", help="Verify SSL certificates."),
|
|
93
|
+
] = None,
|
|
89
94
|
force: Annotated[
|
|
90
95
|
bool,
|
|
91
96
|
typer.Option("--force", "-f", help="Clean up existing resources first."),
|
|
@@ -147,17 +152,13 @@ def load(
|
|
|
147
152
|
typer.Option(
|
|
148
153
|
"--type",
|
|
149
154
|
"-t",
|
|
150
|
-
help="Source type: website, sitemap (auto-detects if not specified).",
|
|
155
|
+
help="Source type: website, sitemap, discovery (auto-detects if not specified).",
|
|
151
156
|
),
|
|
152
157
|
] = None,
|
|
153
158
|
index: Annotated[
|
|
154
159
|
str,
|
|
155
|
-
typer.Option("--index", "-i", help="Target index name."),
|
|
160
|
+
typer.Option("--index", "-i", help="Target index name (use tenant-specific name for multi-tenancy)."),
|
|
156
161
|
] = "knowledge",
|
|
157
|
-
account_id: Annotated[
|
|
158
|
-
Optional[str],
|
|
159
|
-
typer.Option("--account-id", "-a", help="Multi-tenant account ID."),
|
|
160
|
-
] = None,
|
|
161
162
|
collection_id: Annotated[
|
|
162
163
|
Optional[str],
|
|
163
164
|
typer.Option("--collection-id", "-c", help="Collection grouping ID."),
|
|
@@ -186,16 +187,50 @@ def load(
|
|
|
186
187
|
bool,
|
|
187
188
|
typer.Option("--verbose", "-V", help="Show per-document progress."),
|
|
188
189
|
] = False,
|
|
190
|
+
discovery: Annotated[
|
|
191
|
+
bool,
|
|
192
|
+
typer.Option(
|
|
193
|
+
"--discovery",
|
|
194
|
+
"-D",
|
|
195
|
+
help="Use discovery loader to crawl and discover all URLs from the website.",
|
|
196
|
+
),
|
|
197
|
+
] = False,
|
|
198
|
+
max_depth: Annotated[
|
|
199
|
+
int,
|
|
200
|
+
typer.Option("--max-depth", help="Maximum crawl depth for discovery (default: 3)."),
|
|
201
|
+
] = 3,
|
|
202
|
+
max_pages: Annotated[
|
|
203
|
+
int,
|
|
204
|
+
typer.Option("--max-pages", help="Maximum pages to discover (default: 100)."),
|
|
205
|
+
] = 100,
|
|
206
|
+
same_domain: Annotated[
|
|
207
|
+
bool,
|
|
208
|
+
typer.Option(
|
|
209
|
+
"--same-domain/--any-domain",
|
|
210
|
+
help="Only crawl URLs on the same domain (default: same domain only).",
|
|
211
|
+
),
|
|
212
|
+
] = True,
|
|
189
213
|
) -> None:
|
|
190
214
|
"""Load and index content from URLs or sitemaps.
|
|
191
215
|
|
|
192
216
|
Fetches content, chunks it for optimal embedding, and indexes
|
|
193
217
|
into OpenSearch with automatic embedding generation.
|
|
194
218
|
|
|
219
|
+
[bold]Multi-tenancy:[/bold]
|
|
220
|
+
Use --index with tenant-specific index names for isolation
|
|
221
|
+
(e.g., --index knowledge-{account_id}). Each tenant's data
|
|
222
|
+
is stored in a separate index for complete isolation.
|
|
223
|
+
|
|
224
|
+
[bold]Discovery Mode:[/bold]
|
|
225
|
+
Use --discovery to crawl and discover all URLs from a website
|
|
226
|
+
before loading. This is useful for sites without a sitemap.
|
|
227
|
+
|
|
195
228
|
[bold]Example:[/bold]
|
|
196
229
|
$ gnosisllm-knowledge load https://docs.example.com/intro
|
|
197
230
|
$ gnosisllm-knowledge load https://example.com/sitemap.xml --type sitemap
|
|
198
231
|
$ gnosisllm-knowledge load https://docs.example.com/sitemap.xml --max-urls 500
|
|
232
|
+
$ gnosisllm-knowledge load https://docs.example.com --discovery --max-depth 5
|
|
233
|
+
$ gnosisllm-knowledge load https://docs.example.com --index knowledge-tenant-123
|
|
199
234
|
"""
|
|
200
235
|
from gnosisllm_knowledge.cli.commands.load import load_command
|
|
201
236
|
|
|
@@ -205,7 +240,6 @@ def load(
|
|
|
205
240
|
source=source,
|
|
206
241
|
source_type=source_type,
|
|
207
242
|
index_name=index,
|
|
208
|
-
account_id=account_id,
|
|
209
243
|
collection_id=collection_id,
|
|
210
244
|
source_id=source_id,
|
|
211
245
|
batch_size=batch_size,
|
|
@@ -213,6 +247,10 @@ def load(
|
|
|
213
247
|
force=force,
|
|
214
248
|
dry_run=dry_run,
|
|
215
249
|
verbose=verbose,
|
|
250
|
+
discovery=discovery,
|
|
251
|
+
max_depth=max_depth,
|
|
252
|
+
max_pages=max_pages,
|
|
253
|
+
same_domain=same_domain,
|
|
216
254
|
)
|
|
217
255
|
)
|
|
218
256
|
|
|
@@ -238,7 +276,7 @@ def search(
|
|
|
238
276
|
] = "hybrid",
|
|
239
277
|
index: Annotated[
|
|
240
278
|
str,
|
|
241
|
-
typer.Option("--index", "-i", help="Index to search."),
|
|
279
|
+
typer.Option("--index", "-i", help="Index to search (use tenant-specific name for multi-tenancy)."),
|
|
242
280
|
] = "knowledge",
|
|
243
281
|
limit: Annotated[
|
|
244
282
|
int,
|
|
@@ -248,10 +286,6 @@ def search(
|
|
|
248
286
|
int,
|
|
249
287
|
typer.Option("--offset", "-o", help="Pagination offset."),
|
|
250
288
|
] = 0,
|
|
251
|
-
account_id: Annotated[
|
|
252
|
-
Optional[str],
|
|
253
|
-
typer.Option("--account-id", "-a", help="Filter by account ID."),
|
|
254
|
-
] = None,
|
|
255
289
|
collection_ids: Annotated[
|
|
256
290
|
Optional[str],
|
|
257
291
|
typer.Option("--collection-ids", "-c", help="Filter by collection IDs (comma-separated)."),
|
|
@@ -289,10 +323,16 @@ def search(
|
|
|
289
323
|
- [cyan]hybrid[/cyan]: Combined semantic + keyword (default, best results)
|
|
290
324
|
- [cyan]agentic[/cyan]: AI-powered search with reasoning
|
|
291
325
|
|
|
326
|
+
[bold]Multi-tenancy:[/bold]
|
|
327
|
+
Use --index with tenant-specific index names for isolation
|
|
328
|
+
(e.g., --index knowledge-{account_id}). Each tenant's data
|
|
329
|
+
is stored in a separate index for complete isolation.
|
|
330
|
+
|
|
292
331
|
[bold]Example:[/bold]
|
|
293
332
|
$ gnosisllm-knowledge search "how to configure auth"
|
|
294
333
|
$ gnosisllm-knowledge search "API reference" --mode semantic --limit 10
|
|
295
334
|
$ gnosisllm-knowledge search --interactive
|
|
335
|
+
$ gnosisllm-knowledge search "query" --index knowledge-tenant-123
|
|
296
336
|
"""
|
|
297
337
|
from gnosisllm_knowledge.cli.commands.search import search_command
|
|
298
338
|
|
|
@@ -304,7 +344,6 @@ def search(
|
|
|
304
344
|
index_name=index,
|
|
305
345
|
limit=limit,
|
|
306
346
|
offset=offset,
|
|
307
|
-
account_id=account_id,
|
|
308
347
|
collection_ids=collection_ids,
|
|
309
348
|
source_ids=source_ids,
|
|
310
349
|
min_score=min_score,
|
|
@@ -367,6 +406,18 @@ def info() -> None:
|
|
|
367
406
|
|
|
368
407
|
display.newline()
|
|
369
408
|
|
|
409
|
+
display.table(
|
|
410
|
+
"Agentic Memory Configuration",
|
|
411
|
+
[
|
|
412
|
+
("LLM Model ID", config.memory_llm_model_id or "[dim]Not set[/dim]"),
|
|
413
|
+
("Embedding Model ID", config.memory_embedding_model_id or "[dim]Not set[/dim]"),
|
|
414
|
+
("LLM Model", config.memory_llm_model),
|
|
415
|
+
("Embedding Model", config.memory_embedding_model),
|
|
416
|
+
],
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
display.newline()
|
|
420
|
+
|
|
370
421
|
display.table(
|
|
371
422
|
"Content Fetching",
|
|
372
423
|
[
|
|
@@ -439,7 +490,7 @@ def agentic_setup(
|
|
|
439
490
|
def agentic_chat(
|
|
440
491
|
index: Annotated[
|
|
441
492
|
str,
|
|
442
|
-
typer.Option("--index", "-i", help="Index to search."),
|
|
493
|
+
typer.Option("--index", "-i", help="Index to search (use tenant-specific name for multi-tenancy)."),
|
|
443
494
|
] = "knowledge",
|
|
444
495
|
agent_type: Annotated[
|
|
445
496
|
str,
|
|
@@ -449,10 +500,6 @@ def agentic_chat(
|
|
|
449
500
|
help="Agent type: flow or conversational (default).",
|
|
450
501
|
),
|
|
451
502
|
] = "conversational",
|
|
452
|
-
account_id: Annotated[
|
|
453
|
-
Optional[str],
|
|
454
|
-
typer.Option("--account-id", "-a", help="Filter by account ID."),
|
|
455
|
-
] = None,
|
|
456
503
|
collection_ids: Annotated[
|
|
457
504
|
Optional[str],
|
|
458
505
|
typer.Option("--collection-ids", "-c", help="Filter by collection IDs (comma-separated)."),
|
|
@@ -467,10 +514,15 @@ def agentic_chat(
|
|
|
467
514
|
Start a conversation with the AI-powered knowledge assistant.
|
|
468
515
|
The agent remembers context for multi-turn dialogue.
|
|
469
516
|
|
|
517
|
+
[bold]Multi-tenancy:[/bold]
|
|
518
|
+
Use --index with tenant-specific index names for isolation
|
|
519
|
+
(e.g., --index knowledge-{account_id}).
|
|
520
|
+
|
|
470
521
|
[bold]Example:[/bold]
|
|
471
522
|
$ gnosisllm-knowledge agentic chat
|
|
472
523
|
$ gnosisllm-knowledge agentic chat --type flow
|
|
473
524
|
$ gnosisllm-knowledge agentic chat --verbose
|
|
525
|
+
$ gnosisllm-knowledge agentic chat --index knowledge-tenant-123
|
|
474
526
|
"""
|
|
475
527
|
from gnosisllm_knowledge.cli.commands.agentic import agentic_chat_command
|
|
476
528
|
|
|
@@ -479,7 +531,6 @@ def agentic_chat(
|
|
|
479
531
|
display=display,
|
|
480
532
|
index_name=index,
|
|
481
533
|
agent_type=agent_type,
|
|
482
|
-
account_id=account_id,
|
|
483
534
|
collection_ids=collection_ids,
|
|
484
535
|
verbose=verbose,
|
|
485
536
|
)
|
|
@@ -500,6 +551,360 @@ def agentic_status() -> None:
|
|
|
500
551
|
asyncio.run(agentic_status_command(display=display))
|
|
501
552
|
|
|
502
553
|
|
|
554
|
+
# ============================================================================
|
|
555
|
+
# MEMORY SUBCOMMAND GROUP
|
|
556
|
+
# ============================================================================
|
|
557
|
+
|
|
558
|
+
memory_app = typer.Typer(
|
|
559
|
+
name="memory",
|
|
560
|
+
help="Agentic Memory management commands.",
|
|
561
|
+
no_args_is_help=True,
|
|
562
|
+
rich_markup_mode="rich",
|
|
563
|
+
)
|
|
564
|
+
app.add_typer(memory_app, name="memory")
|
|
565
|
+
|
|
566
|
+
# Container sub-subcommand
|
|
567
|
+
container_app = typer.Typer(
|
|
568
|
+
name="container",
|
|
569
|
+
help="Memory container management.",
|
|
570
|
+
no_args_is_help=True,
|
|
571
|
+
rich_markup_mode="rich",
|
|
572
|
+
)
|
|
573
|
+
memory_app.add_typer(container_app, name="container")
|
|
574
|
+
|
|
575
|
+
# Session sub-subcommand
|
|
576
|
+
session_app = typer.Typer(
|
|
577
|
+
name="session",
|
|
578
|
+
help="Session management.",
|
|
579
|
+
no_args_is_help=True,
|
|
580
|
+
rich_markup_mode="rich",
|
|
581
|
+
)
|
|
582
|
+
memory_app.add_typer(session_app, name="session")
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
@memory_app.command("setup")
|
|
586
|
+
def memory_setup(
|
|
587
|
+
openai_key: Annotated[
|
|
588
|
+
Optional[str],
|
|
589
|
+
typer.Option("--openai-key", envvar="OPENAI_API_KEY", help="OpenAI API key for connector setup."),
|
|
590
|
+
] = None,
|
|
591
|
+
llm_model: Annotated[
|
|
592
|
+
str,
|
|
593
|
+
typer.Option("--llm-model", help="LLM model for fact extraction."),
|
|
594
|
+
] = "gpt-4o",
|
|
595
|
+
embedding_model: Annotated[
|
|
596
|
+
str,
|
|
597
|
+
typer.Option("--embedding-model", help="Embedding model name."),
|
|
598
|
+
] = "text-embedding-3-small",
|
|
599
|
+
) -> None:
|
|
600
|
+
"""Setup OpenSearch for Agentic Memory.
|
|
601
|
+
|
|
602
|
+
Creates the required LLM and embedding connectors and models
|
|
603
|
+
for Agentic Memory to work.
|
|
604
|
+
|
|
605
|
+
[bold]Example:[/bold]
|
|
606
|
+
$ gnosisllm-knowledge memory setup --openai-key sk-...
|
|
607
|
+
$ gnosisllm-knowledge memory setup --llm-model gpt-4o --embedding-model text-embedding-3-small
|
|
608
|
+
"""
|
|
609
|
+
from gnosisllm_knowledge.cli.commands.memory import memory_setup_command
|
|
610
|
+
|
|
611
|
+
asyncio.run(
|
|
612
|
+
memory_setup_command(
|
|
613
|
+
display=display,
|
|
614
|
+
openai_key=openai_key,
|
|
615
|
+
llm_model=llm_model,
|
|
616
|
+
embedding_model=embedding_model,
|
|
617
|
+
)
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
@memory_app.command("status")
|
|
622
|
+
def memory_status() -> None:
|
|
623
|
+
"""Show memory configuration status.
|
|
624
|
+
|
|
625
|
+
Displays configured models and verifies their health.
|
|
626
|
+
|
|
627
|
+
[bold]Example:[/bold]
|
|
628
|
+
$ gnosisllm-knowledge memory status
|
|
629
|
+
"""
|
|
630
|
+
from gnosisllm_knowledge.cli.commands.memory import memory_status_command
|
|
631
|
+
|
|
632
|
+
asyncio.run(memory_status_command(display=display))
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
@memory_app.command("store")
|
|
636
|
+
def memory_store(
|
|
637
|
+
container_id: Annotated[
|
|
638
|
+
str,
|
|
639
|
+
typer.Argument(help="Container ID to store messages in."),
|
|
640
|
+
],
|
|
641
|
+
file: Annotated[
|
|
642
|
+
Optional[str],
|
|
643
|
+
typer.Option("--file", "-f", help="JSON file with messages."),
|
|
644
|
+
] = None,
|
|
645
|
+
user_id: Annotated[
|
|
646
|
+
Optional[str],
|
|
647
|
+
typer.Option("--user-id", help="User ID for namespace."),
|
|
648
|
+
] = None,
|
|
649
|
+
session_id: Annotated[
|
|
650
|
+
Optional[str],
|
|
651
|
+
typer.Option("--session-id", help="Session ID for namespace."),
|
|
652
|
+
] = None,
|
|
653
|
+
infer: Annotated[
|
|
654
|
+
bool,
|
|
655
|
+
typer.Option("--infer/--no-infer", help="Enable/disable fact extraction."),
|
|
656
|
+
] = True,
|
|
657
|
+
json_output: Annotated[
|
|
658
|
+
bool,
|
|
659
|
+
typer.Option("--json", "-j", help="Output as JSON."),
|
|
660
|
+
] = False,
|
|
661
|
+
) -> None:
|
|
662
|
+
"""Store conversation in memory.
|
|
663
|
+
|
|
664
|
+
Stores messages in working memory and optionally extracts facts
|
|
665
|
+
to long-term memory using LLM inference.
|
|
666
|
+
|
|
667
|
+
[bold]Example:[/bold]
|
|
668
|
+
$ gnosisllm-knowledge memory store <container-id> -f messages.json --user-id alice
|
|
669
|
+
$ gnosisllm-knowledge memory store <container-id> -f messages.json --no-infer
|
|
670
|
+
"""
|
|
671
|
+
from gnosisllm_knowledge.cli.commands.memory import memory_store_command
|
|
672
|
+
|
|
673
|
+
asyncio.run(
|
|
674
|
+
memory_store_command(
|
|
675
|
+
display=display,
|
|
676
|
+
container_id=container_id,
|
|
677
|
+
file=file,
|
|
678
|
+
user_id=user_id,
|
|
679
|
+
session_id=session_id,
|
|
680
|
+
infer=infer,
|
|
681
|
+
json_output=json_output,
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
@memory_app.command("recall")
|
|
687
|
+
def memory_recall(
|
|
688
|
+
container_id: Annotated[
|
|
689
|
+
str,
|
|
690
|
+
typer.Argument(help="Container ID to search."),
|
|
691
|
+
],
|
|
692
|
+
query: Annotated[
|
|
693
|
+
str,
|
|
694
|
+
typer.Argument(help="Search query text."),
|
|
695
|
+
],
|
|
696
|
+
user_id: Annotated[
|
|
697
|
+
Optional[str],
|
|
698
|
+
typer.Option("--user-id", help="Filter by user ID."),
|
|
699
|
+
] = None,
|
|
700
|
+
session_id: Annotated[
|
|
701
|
+
Optional[str],
|
|
702
|
+
typer.Option("--session-id", help="Filter by session ID."),
|
|
703
|
+
] = None,
|
|
704
|
+
limit: Annotated[
|
|
705
|
+
int,
|
|
706
|
+
typer.Option("--limit", "-n", help="Maximum results."),
|
|
707
|
+
] = 10,
|
|
708
|
+
json_output: Annotated[
|
|
709
|
+
bool,
|
|
710
|
+
typer.Option("--json", "-j", help="Output as JSON."),
|
|
711
|
+
] = False,
|
|
712
|
+
) -> None:
|
|
713
|
+
"""Search long-term memory.
|
|
714
|
+
|
|
715
|
+
Performs semantic search over extracted facts and memories.
|
|
716
|
+
|
|
717
|
+
[bold]Example:[/bold]
|
|
718
|
+
$ gnosisllm-knowledge memory recall <container-id> "user preferences" --user-id alice
|
|
719
|
+
$ gnosisllm-knowledge memory recall <container-id> "food preferences" --limit 5 --json
|
|
720
|
+
"""
|
|
721
|
+
from gnosisllm_knowledge.cli.commands.memory import memory_recall_command
|
|
722
|
+
|
|
723
|
+
asyncio.run(
|
|
724
|
+
memory_recall_command(
|
|
725
|
+
display=display,
|
|
726
|
+
container_id=container_id,
|
|
727
|
+
query=query,
|
|
728
|
+
user_id=user_id,
|
|
729
|
+
session_id=session_id,
|
|
730
|
+
limit=limit,
|
|
731
|
+
json_output=json_output,
|
|
732
|
+
)
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
@memory_app.command("stats")
|
|
737
|
+
def memory_stats(
|
|
738
|
+
container_id: Annotated[
|
|
739
|
+
str,
|
|
740
|
+
typer.Argument(help="Container ID."),
|
|
741
|
+
],
|
|
742
|
+
json_output: Annotated[
|
|
743
|
+
bool,
|
|
744
|
+
typer.Option("--json", "-j", help="Output as JSON."),
|
|
745
|
+
] = False,
|
|
746
|
+
) -> None:
|
|
747
|
+
"""Show container statistics.
|
|
748
|
+
|
|
749
|
+
Displays memory counts, session count, and strategy breakdown.
|
|
750
|
+
|
|
751
|
+
[bold]Example:[/bold]
|
|
752
|
+
$ gnosisllm-knowledge memory stats <container-id>
|
|
753
|
+
$ gnosisllm-knowledge memory stats <container-id> --json
|
|
754
|
+
"""
|
|
755
|
+
from gnosisllm_knowledge.cli.commands.memory import memory_stats_command
|
|
756
|
+
|
|
757
|
+
asyncio.run(
|
|
758
|
+
memory_stats_command(
|
|
759
|
+
display=display,
|
|
760
|
+
container_id=container_id,
|
|
761
|
+
json_output=json_output,
|
|
762
|
+
)
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
# === Container Commands ===
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
@container_app.command("create")
|
|
770
|
+
def container_create(
|
|
771
|
+
name: Annotated[
|
|
772
|
+
str,
|
|
773
|
+
typer.Argument(help="Container name."),
|
|
774
|
+
],
|
|
775
|
+
description: Annotated[
|
|
776
|
+
Optional[str],
|
|
777
|
+
typer.Option("--description", "-d", help="Container description."),
|
|
778
|
+
] = None,
|
|
779
|
+
config_file: Annotated[
|
|
780
|
+
Optional[str],
|
|
781
|
+
typer.Option("--config", "-c", help="JSON file with strategy configuration."),
|
|
782
|
+
] = None,
|
|
783
|
+
) -> None:
|
|
784
|
+
"""Create a new memory container.
|
|
785
|
+
|
|
786
|
+
Containers hold memories with configurable extraction strategies.
|
|
787
|
+
Each strategy is scoped to namespace fields for partitioning.
|
|
788
|
+
|
|
789
|
+
[bold]Example config.json:[/bold]
|
|
790
|
+
{
|
|
791
|
+
"strategies": [
|
|
792
|
+
{"type": "SEMANTIC", "namespace": ["user_id"]},
|
|
793
|
+
{"type": "USER_PREFERENCE", "namespace": ["user_id"]},
|
|
794
|
+
{"type": "SUMMARY", "namespace": ["session_id"]}
|
|
795
|
+
]
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
[bold]Example:[/bold]
|
|
799
|
+
$ gnosisllm-knowledge memory container create my-memory
|
|
800
|
+
$ gnosisllm-knowledge memory container create agent-memory -c config.json
|
|
801
|
+
"""
|
|
802
|
+
from gnosisllm_knowledge.cli.commands.memory import container_create_command
|
|
803
|
+
|
|
804
|
+
asyncio.run(
|
|
805
|
+
container_create_command(
|
|
806
|
+
display=display,
|
|
807
|
+
name=name,
|
|
808
|
+
description=description,
|
|
809
|
+
config_file=config_file,
|
|
810
|
+
)
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
@container_app.command("list")
|
|
815
|
+
def container_list(
|
|
816
|
+
json_output: Annotated[
|
|
817
|
+
bool,
|
|
818
|
+
typer.Option("--json", "-j", help="Output as JSON."),
|
|
819
|
+
] = False,
|
|
820
|
+
) -> None:
|
|
821
|
+
"""List all memory containers.
|
|
822
|
+
|
|
823
|
+
[bold]Example:[/bold]
|
|
824
|
+
$ gnosisllm-knowledge memory container list
|
|
825
|
+
$ gnosisllm-knowledge memory container list --json
|
|
826
|
+
"""
|
|
827
|
+
from gnosisllm_knowledge.cli.commands.memory import container_list_command
|
|
828
|
+
|
|
829
|
+
asyncio.run(
|
|
830
|
+
container_list_command(
|
|
831
|
+
display=display,
|
|
832
|
+
json_output=json_output,
|
|
833
|
+
)
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
@container_app.command("delete")
|
|
838
|
+
def container_delete(
|
|
839
|
+
container_id: Annotated[
|
|
840
|
+
str,
|
|
841
|
+
typer.Argument(help="Container ID to delete."),
|
|
842
|
+
],
|
|
843
|
+
force: Annotated[
|
|
844
|
+
bool,
|
|
845
|
+
typer.Option("--force", "-f", help="Skip confirmation prompt."),
|
|
846
|
+
] = False,
|
|
847
|
+
) -> None:
|
|
848
|
+
"""Delete a memory container.
|
|
849
|
+
|
|
850
|
+
This permanently deletes the container and all its memories.
|
|
851
|
+
|
|
852
|
+
[bold]Example:[/bold]
|
|
853
|
+
$ gnosisllm-knowledge memory container delete <container-id>
|
|
854
|
+
$ gnosisllm-knowledge memory container delete <container-id> --force
|
|
855
|
+
"""
|
|
856
|
+
from gnosisllm_knowledge.cli.commands.memory import container_delete_command
|
|
857
|
+
|
|
858
|
+
asyncio.run(
|
|
859
|
+
container_delete_command(
|
|
860
|
+
display=display,
|
|
861
|
+
container_id=container_id,
|
|
862
|
+
force=force,
|
|
863
|
+
)
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
# === Session Commands ===
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
@session_app.command("list")
|
|
871
|
+
def session_list(
|
|
872
|
+
container_id: Annotated[
|
|
873
|
+
str,
|
|
874
|
+
typer.Argument(help="Container ID."),
|
|
875
|
+
],
|
|
876
|
+
user_id: Annotated[
|
|
877
|
+
Optional[str],
|
|
878
|
+
typer.Option("--user-id", help="Filter by user ID."),
|
|
879
|
+
] = None,
|
|
880
|
+
limit: Annotated[
|
|
881
|
+
int,
|
|
882
|
+
typer.Option("--limit", "-n", help="Maximum sessions."),
|
|
883
|
+
] = 20,
|
|
884
|
+
json_output: Annotated[
|
|
885
|
+
bool,
|
|
886
|
+
typer.Option("--json", "-j", help="Output as JSON."),
|
|
887
|
+
] = False,
|
|
888
|
+
) -> None:
|
|
889
|
+
"""List sessions in a container.
|
|
890
|
+
|
|
891
|
+
[bold]Example:[/bold]
|
|
892
|
+
$ gnosisllm-knowledge memory session list <container-id>
|
|
893
|
+
$ gnosisllm-knowledge memory session list <container-id> --user-id alice
|
|
894
|
+
"""
|
|
895
|
+
from gnosisllm_knowledge.cli.commands.memory import session_list_command
|
|
896
|
+
|
|
897
|
+
asyncio.run(
|
|
898
|
+
session_list_command(
|
|
899
|
+
display=display,
|
|
900
|
+
container_id=container_id,
|
|
901
|
+
user_id=user_id,
|
|
902
|
+
limit=limit,
|
|
903
|
+
json_output=json_output,
|
|
904
|
+
)
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
|
|
503
908
|
def main() -> None:
|
|
504
909
|
"""CLI entry point."""
|
|
505
910
|
app()
|
|
@@ -4,6 +4,10 @@ Commands:
|
|
|
4
4
|
- setup: Configure agents in OpenSearch
|
|
5
5
|
- chat: Interactive agentic chat session
|
|
6
6
|
- status: Show agent configuration status
|
|
7
|
+
|
|
8
|
+
Note:
|
|
9
|
+
This library is tenant-agnostic. Multi-tenancy is achieved through index
|
|
10
|
+
isolation - each tenant should use a separate index (e.g., "knowledge-{account_id}").
|
|
7
11
|
"""
|
|
8
12
|
|
|
9
13
|
from __future__ import annotations
|
|
@@ -105,6 +109,17 @@ async def agentic_setup_command(
|
|
|
105
109
|
if agent_type in ("conversational", "all"):
|
|
106
110
|
agent_types_to_setup.append("conversational")
|
|
107
111
|
|
|
112
|
+
# If force, cleanup existing agents first
|
|
113
|
+
if force:
|
|
114
|
+
display.info("Force mode: cleaning up existing agents...")
|
|
115
|
+
try:
|
|
116
|
+
cleanup_result = await adapter.cleanup_agents()
|
|
117
|
+
for step in cleanup_result.steps_completed:
|
|
118
|
+
display.success(step)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
display.warning(f"Cleanup warning (continuing): {e}")
|
|
121
|
+
display.newline()
|
|
122
|
+
|
|
108
123
|
# Build step list
|
|
109
124
|
steps = []
|
|
110
125
|
if "flow" in agent_types_to_setup:
|
|
@@ -191,17 +206,19 @@ async def agentic_chat_command(
|
|
|
191
206
|
display: RichDisplayService,
|
|
192
207
|
index_name: str = "knowledge",
|
|
193
208
|
agent_type: str = "conversational",
|
|
194
|
-
account_id: str | None = None,
|
|
195
209
|
collection_ids: str | None = None,
|
|
196
210
|
verbose: bool = False,
|
|
197
211
|
) -> None:
|
|
198
212
|
"""Interactive agentic chat session.
|
|
199
213
|
|
|
214
|
+
Note:
|
|
215
|
+
Multi-tenancy is achieved through index isolation. Use tenant-specific
|
|
216
|
+
index names instead (e.g., --index knowledge-tenant-123).
|
|
217
|
+
|
|
200
218
|
Args:
|
|
201
219
|
display: Display service for output.
|
|
202
|
-
index_name: Index to search.
|
|
220
|
+
index_name: Index to search (use tenant-specific name for isolation).
|
|
203
221
|
agent_type: Agent type ('flow' or 'conversational').
|
|
204
|
-
account_id: Filter by account ID.
|
|
205
222
|
collection_ids: Filter by collection IDs (comma-separated).
|
|
206
223
|
verbose: Show reasoning steps.
|
|
207
224
|
"""
|
|
@@ -231,7 +248,6 @@ async def agentic_chat_command(
|
|
|
231
248
|
if agent_type == "conversational":
|
|
232
249
|
return await searcher.create_conversation(
|
|
233
250
|
name="CLI Chat Session",
|
|
234
|
-
account_id=account_id,
|
|
235
251
|
)
|
|
236
252
|
return None
|
|
237
253
|
|
|
@@ -280,7 +296,6 @@ async def agentic_chat_command(
|
|
|
280
296
|
agent_type=AgentType.CONVERSATIONAL if agent_type == "conversational" else AgentType.FLOW,
|
|
281
297
|
conversation_id=conversation_id,
|
|
282
298
|
collection_ids=collection_list,
|
|
283
|
-
account_id=account_id,
|
|
284
299
|
include_reasoning=verbose,
|
|
285
300
|
)
|
|
286
301
|
|
|
@@ -384,7 +399,6 @@ async def agentic_search_command(
|
|
|
384
399
|
query: str,
|
|
385
400
|
index_name: str = "knowledge",
|
|
386
401
|
agent_type: str = "flow",
|
|
387
|
-
account_id: str | None = None,
|
|
388
402
|
collection_ids: str | None = None,
|
|
389
403
|
source_ids: str | None = None,
|
|
390
404
|
limit: int = 5,
|
|
@@ -393,12 +407,15 @@ async def agentic_search_command(
|
|
|
393
407
|
) -> dict[str, Any] | None:
|
|
394
408
|
"""Execute agentic search.
|
|
395
409
|
|
|
410
|
+
Note:
|
|
411
|
+
Multi-tenancy is achieved through index isolation. Use tenant-specific
|
|
412
|
+
index names instead (e.g., --index knowledge-tenant-123).
|
|
413
|
+
|
|
396
414
|
Args:
|
|
397
415
|
display: Display service for output.
|
|
398
416
|
query: Search query text.
|
|
399
|
-
index_name: Index to search.
|
|
417
|
+
index_name: Index to search (use tenant-specific name for isolation).
|
|
400
418
|
agent_type: Agent type ('flow' or 'conversational').
|
|
401
|
-
account_id: Filter by account ID.
|
|
402
419
|
collection_ids: Filter by collection IDs (comma-separated).
|
|
403
420
|
source_ids: Filter by source IDs (comma-separated).
|
|
404
421
|
limit: Maximum source documents to retrieve.
|
|
@@ -436,12 +453,12 @@ async def agentic_search_command(
|
|
|
436
453
|
)
|
|
437
454
|
|
|
438
455
|
# Build query
|
|
456
|
+
# Note: account_id is deprecated and ignored - use index isolation instead
|
|
439
457
|
agentic_query = AgenticSearchQuery(
|
|
440
458
|
text=query,
|
|
441
459
|
agent_type=AgentType.CONVERSATIONAL if agent_type == "conversational" else AgentType.FLOW,
|
|
442
460
|
collection_ids=collection_list,
|
|
443
461
|
source_ids=source_list,
|
|
444
|
-
account_id=account_id,
|
|
445
462
|
limit=limit,
|
|
446
463
|
include_reasoning=verbose,
|
|
447
464
|
)
|