gnosisllm-knowledge 0.4.0__py3-none-any.whl → 0.4.3__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.
@@ -119,6 +119,7 @@ class Knowledge:
119
119
  loader_factory: LoaderFactory | None = None,
120
120
  default_index: str | None = None,
121
121
  events: EventEmitter | None = None,
122
+ config: OpenSearchConfig | None = None,
122
123
  ) -> None:
123
124
  """Initialize Knowledge with components.
124
125
 
@@ -131,6 +132,7 @@ class Knowledge:
131
132
  loader_factory: Optional loader factory.
132
133
  default_index: Default index name.
133
134
  events: Optional event emitter.
135
+ config: Optional OpenSearch config for settings like batch sizes.
134
136
 
135
137
  Note:
136
138
  Embeddings are generated automatically by OpenSearch ingest pipeline.
@@ -144,6 +146,7 @@ class Knowledge:
144
146
  self._loader_factory = loader_factory
145
147
  self._default_index = default_index
146
148
  self._events = events or EventEmitter()
149
+ self._config = config
147
150
 
148
151
  # Initialize services lazily
149
152
  self._indexing_service: KnowledgeIndexingService | None = None
@@ -262,6 +265,7 @@ class Knowledge:
262
265
  fetcher=fetcher,
263
266
  loader_factory=loader_factory,
264
267
  default_index=config.knowledge_index_name,
268
+ config=config,
265
269
  )
266
270
 
267
271
  @classmethod
@@ -431,11 +435,15 @@ class Knowledge:
431
435
  events=self._events,
432
436
  )
433
437
 
438
+ # Get batch size from config or use default
439
+ batch_size = self._config.indexing_batch_size if self._config else 10
440
+
434
441
  return await service.load_and_index(
435
442
  source=source,
436
443
  index_name=index,
437
444
  collection_id=collection_id,
438
445
  source_id=source_id,
446
+ batch_size=batch_size,
439
447
  **options,
440
448
  )
441
449
 
@@ -395,16 +395,37 @@ class MemorySearcher:
395
395
  "offset": offset,
396
396
  }
397
397
 
398
- def count(self, index_name: str) -> int:
399
- """Count documents in index.
398
+ async def count(
399
+ self,
400
+ index_name: str,
401
+ collection_id: str | None = None,
402
+ source_id: str | None = None,
403
+ ) -> int:
404
+ """Count documents in index with optional filters.
400
405
 
401
406
  Args:
402
407
  index_name: Index to count.
408
+ collection_id: Filter by collection.
409
+ source_id: Filter by source.
403
410
 
404
411
  Returns:
405
412
  Document count.
406
413
  """
407
- return self._indexer.count(index_name)
414
+ # Use efficient O(1) count when no filters
415
+ if not collection_id and not source_id:
416
+ return self._indexer.count(index_name)
417
+
418
+ # With filters, iterate over index values (memory backend is for testing only)
419
+ index_data = self._indexer._indices.get(index_name, {})
420
+ count = 0
421
+ for doc in index_data.values():
422
+ if collection_id and doc.get("collection_id") != collection_id:
423
+ continue
424
+ if source_id and doc.get("source_id") != source_id:
425
+ continue
426
+ count += 1
427
+
428
+ return count
408
429
 
409
430
  async def get_collections(self, index_name: str) -> list[dict[str, Any]]:
410
431
  """Get unique collections with document counts.
@@ -109,6 +109,11 @@ class OpenSearchConfig:
109
109
  bulk_batch_size: int = 500
110
110
  bulk_max_concurrent: int = 3
111
111
 
112
+ # === Indexing Service ===
113
+ # Batch size for progressive indexing during load operations
114
+ # Documents are indexed in batches of this size as they stream in
115
+ indexing_batch_size: int = 10
116
+
112
117
  @property
113
118
  def url(self) -> str:
114
119
  """Get the full OpenSearch URL."""
@@ -213,4 +218,6 @@ class OpenSearchConfig:
213
218
  # === Bulk Indexing ===
214
219
  bulk_batch_size=int(os.getenv("OPENSEARCH_BULK_BATCH_SIZE", "500")),
215
220
  bulk_max_concurrent=int(os.getenv("OPENSEARCH_BULK_MAX_CONCURRENT", "3")),
221
+ # === Indexing Service ===
222
+ indexing_batch_size=int(os.getenv("GNOSISLLM_INDEXING_BATCH_SIZE", "10")),
216
223
  )
@@ -506,6 +506,61 @@ class OpenSearchKnowledgeSearcher:
506
506
  "error": str(e),
507
507
  }
508
508
 
509
+ async def count(
510
+ self,
511
+ index_name: str,
512
+ collection_id: str | None = None,
513
+ source_id: str | None = None,
514
+ ) -> int:
515
+ """Count documents in index with optional filters.
516
+
517
+ Uses native _count API instead of search for efficiency and to avoid
518
+ hybrid search issues with empty queries.
519
+
520
+ Args:
521
+ index_name: Index to query.
522
+ collection_id: Filter by collection.
523
+ source_id: Filter by source.
524
+
525
+ Returns:
526
+ Document count.
527
+ """
528
+ try:
529
+ # Check if index exists first
530
+ exists = await self._client.indices.exists(index=index_name)
531
+ if not exists:
532
+ logger.debug(f"Index {index_name} does not exist, returning count 0")
533
+ return 0
534
+
535
+ # Build query with optional filters
536
+ query: dict[str, Any] = {"match_all": {}}
537
+
538
+ filters = []
539
+ if collection_id:
540
+ filters.append({"term": {"collection_id": collection_id}})
541
+ if source_id:
542
+ filters.append({"term": {"source_id": source_id}})
543
+
544
+ if filters:
545
+ query = {"bool": {"filter": filters}}
546
+
547
+ # Use native _count API
548
+ response = await self._client.count(
549
+ index=index_name,
550
+ body={"query": query},
551
+ )
552
+
553
+ count = response.get("count", 0)
554
+ logger.debug(f"Count for {index_name}: {count} (collection={collection_id}, source={source_id})")
555
+ return count
556
+
557
+ except Exception as e:
558
+ logger.error(f"Failed to count documents in {index_name}: {e}")
559
+ raise SearchError(
560
+ message=f"Count failed: {e}",
561
+ details={"index": index_name, "collection_id": collection_id, "source_id": source_id},
562
+ ) from e
563
+
509
564
  async def list_documents(
510
565
  self,
511
566
  index_name: str,
@@ -249,10 +249,9 @@ class OpenSearchSetupAdapter:
249
249
  self._model_id = self._config.model_id
250
250
 
251
251
  # Step 4: Create ingest pipeline
252
- # Only create ingest pipeline for global setup (not per-account)
253
- # Account indices should use the global pipeline to ensure consistent model
254
- is_global_setup = self._config.index_prefix == "gnosisllm"
255
- if self._model_id and is_global_setup:
252
+ # Create pipeline for any setup that has a model deployed
253
+ # Each index_prefix namespace gets its own pipeline
254
+ if self._model_id:
256
255
  try:
257
256
  await self._create_ingest_pipeline()
258
257
  pipeline_name = self._config.ingest_pipeline_name or f"{self._config.index_prefix}-ingest-pipeline"
@@ -261,35 +260,33 @@ class OpenSearchSetupAdapter:
261
260
  errors.append(f"Failed to create ingest pipeline: {e}")
262
261
  logger.error(f"Failed to create ingest pipeline: {e}")
263
262
 
264
- # Step 5: Create search pipeline (only for global setup)
265
- if is_global_setup:
266
- try:
267
- await self._create_search_pipeline()
268
- pipeline_name = self._config.search_pipeline_name or f"{self._config.index_prefix}-search-pipeline"
269
- steps_completed.append(f"Created search pipeline: {pipeline_name}")
270
- except Exception as e:
271
- errors.append(f"Failed to create search pipeline: {e}")
272
- logger.error(f"Failed to create search pipeline: {e}")
263
+ # Step 5: Create search pipeline for hybrid search
264
+ try:
265
+ await self._create_search_pipeline()
266
+ pipeline_name = self._config.search_pipeline_name or f"{self._config.index_prefix}-search-pipeline"
267
+ steps_completed.append(f"Created search pipeline: {pipeline_name}")
268
+ except Exception as e:
269
+ errors.append(f"Failed to create search pipeline: {e}")
270
+ logger.error(f"Failed to create search pipeline: {e}")
273
271
 
274
- # Step 6: Create index template (only for global setup)
275
- # Template covers all gnosisllm-* indices including per-account indices
276
- if is_global_setup:
277
- try:
278
- template_name = f"{self._config.index_prefix}-template"
279
- template_body = get_index_template(self._config)
272
+ # Step 6: Create index template for this namespace
273
+ # Template covers all {index_prefix}-* indices
274
+ try:
275
+ template_name = f"{self._config.index_prefix}-template"
276
+ template_body = get_index_template(self._config)
280
277
 
281
- # Ensure template has global pipeline for auto-index creation
282
- global_pipeline = self._config.ingest_pipeline_name or "gnosisllm-ingest-pipeline"
283
- template_body["template"]["settings"]["index"]["default_pipeline"] = global_pipeline
278
+ # Set default pipeline for auto-index creation within this namespace
279
+ default_pipeline = self._config.ingest_pipeline_name or f"{self._config.index_prefix}-ingest-pipeline"
280
+ template_body["template"]["settings"]["index"]["default_pipeline"] = default_pipeline
284
281
 
285
- await self._client.indices.put_index_template(
286
- name=template_name,
287
- body=template_body,
288
- )
289
- steps_completed.append(f"Created index template: {template_name}")
290
- except Exception as e:
291
- errors.append(f"Failed to create index template: {e}")
292
- logger.error(f"Failed to create index template: {e}")
282
+ await self._client.indices.put_index_template(
283
+ name=template_name,
284
+ body=template_body,
285
+ )
286
+ steps_completed.append(f"Created index template: {template_name}")
287
+ except Exception as e:
288
+ errors.append(f"Failed to create index template: {e}")
289
+ logger.error(f"Failed to create index template: {e}")
293
290
 
294
291
  # Step 7: Create knowledge index
295
292
  try:
@@ -298,9 +295,8 @@ class OpenSearchSetupAdapter:
298
295
 
299
296
  if not exists:
300
297
  settings = get_knowledge_index_settings(self._config)
301
- # Add default pipeline - always use global pipeline for consistency
302
- # This ensures all accounts use the same embedding model
303
- pipeline_name = self._config.ingest_pipeline_name or "gnosisllm-ingest-pipeline"
298
+ # Add default pipeline for this namespace
299
+ pipeline_name = self._config.ingest_pipeline_name or f"{self._config.index_prefix}-ingest-pipeline"
304
300
  settings["index"]["default_pipeline"] = pipeline_name
305
301
 
306
302
  await self._client.indices.create(
@@ -185,3 +185,23 @@ class IKnowledgeSearcher(Protocol):
185
185
  List of SearchResults in same order as queries.
186
186
  """
187
187
  ...
188
+
189
+ async def count(
190
+ self,
191
+ index_name: str,
192
+ collection_id: str | None = None,
193
+ source_id: str | None = None,
194
+ ) -> int:
195
+ """Count documents in index with optional filters.
196
+
197
+ Uses native count API instead of search for efficiency.
198
+
199
+ Args:
200
+ index_name: Target index name.
201
+ collection_id: Filter by collection.
202
+ source_id: Filter by source.
203
+
204
+ Returns:
205
+ Document count.
206
+ """
207
+ ...
@@ -119,6 +119,16 @@ class KnowledgeIndexingService:
119
119
  source_id = source_id or str(uuid.uuid4())
120
120
  document_defaults = options.pop("document_defaults", {})
121
121
 
122
+ # Extract metadata from document_defaults to merge with doc.metadata later
123
+ # This allows callers to pass custom metadata (e.g., parent_collection_id)
124
+ # without conflicting with the explicit metadata= parameter
125
+ extra_metadata = document_defaults.pop("metadata", {})
126
+
127
+ # Ensure index exists with correct mappings before indexing
128
+ # This prevents OpenSearch from auto-creating the index with dynamic mapping
129
+ # which would map keyword fields (like collection_id) as text fields
130
+ await self._indexer.ensure_index(index_name)
131
+
122
132
  # Emit batch started event
123
133
  await self._events.emit_async(
124
134
  BatchStartedEvent(
@@ -139,6 +149,8 @@ class KnowledgeIndexingService:
139
149
  # Note: Loader already chunks content, so we don't re-chunk here
140
150
  async for doc in self._loader.load_streaming(source, **options):
141
151
  # Enrich document with collection info
152
+ # Merge doc.metadata with extra_metadata from document_defaults
153
+ merged_metadata = {**doc.metadata, **extra_metadata}
142
154
  enriched_doc = Document(
143
155
  content=doc.content,
144
156
  source=source,
@@ -151,7 +163,7 @@ class KnowledgeIndexingService:
151
163
  total_chunks=doc.total_chunks,
152
164
  parent_doc_id=doc.parent_doc_id,
153
165
  status=DocumentStatus.INDEXED,
154
- metadata=doc.metadata,
166
+ metadata=merged_metadata,
155
167
  **document_defaults,
156
168
  )
157
169
 
@@ -223,6 +235,9 @@ class KnowledgeIndexingService:
223
235
  Returns:
224
236
  Index result.
225
237
  """
238
+ # Ensure index exists with correct mappings before indexing
239
+ await self._indexer.ensure_index(index_name)
240
+
226
241
  total_indexed = 0
227
242
  total_failed = 0
228
243
  errors: list[str] = []
@@ -328,6 +328,9 @@ class KnowledgeSearchService:
328
328
  ) -> int:
329
329
  """Count documents in index.
330
330
 
331
+ Uses native count API instead of search for efficiency and to avoid
332
+ hybrid search issues with empty queries.
333
+
331
334
  Note:
332
335
  This method is tenant-agnostic. Multi-tenancy should be handled
333
336
  at the API layer by using separate indices per account.
@@ -344,18 +347,12 @@ class KnowledgeSearchService:
344
347
  if not index:
345
348
  raise SearchError(message="No index specified")
346
349
 
347
- # Build count query with optional filters
348
- query = SearchQuery(
349
- text="",
350
- limit=0,
351
- collection_ids=[collection_id] if collection_id else None,
352
- source_ids=[source_id] if source_id else None,
350
+ return await self._searcher.count(
351
+ index_name=index,
352
+ collection_id=collection_id,
353
+ source_id=source_id,
353
354
  )
354
355
 
355
- # Use a simple match_all to get total count
356
- result = await self._searcher.search(query, index)
357
- return result.total_hits
358
-
359
356
  async def get_collections(
360
357
  self,
361
358
  index_name: str | None = None,
@@ -173,6 +173,12 @@ class StreamingIndexingPipeline:
173
173
  DeprecationWarning,
174
174
  stacklevel=2,
175
175
  )
176
+
177
+ # Ensure index exists with correct mappings before indexing
178
+ # This prevents OpenSearch from auto-creating the index with dynamic mapping
179
+ # which would map keyword fields (like collection_id) as text fields
180
+ await self._indexer.ensure_index(index_name)
181
+
176
182
  start_time = time.time()
177
183
  self._progress = StreamingProgress(current_phase="starting")
178
184
  await self._emit_progress()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gnosisllm-knowledge
3
- Version: 0.4.0
3
+ Version: 0.4.3
4
4
  Summary: Enterprise-grade knowledge loading, indexing, and search for Python
5
5
  License: MIT
6
6
  Keywords: knowledge-base,rag,semantic-search,vector-search,opensearch,llm,embeddings,enterprise
@@ -1,14 +1,14 @@
1
1
  gnosisllm_knowledge/__init__.py,sha256=Egfn6ROa9OttWweCBVocNCUivKwWfdeFEFgpwAoo_ww,4809
2
2
  gnosisllm_knowledge/api/__init__.py,sha256=xVG6wMjbFa7_qDVRKlp43O4f0EQmPr9w3pAOGmmh32U,200
3
- gnosisllm_knowledge/api/knowledge.py,sha256=sjBXBe__wRdGoawmLfDKw9W2o8efOTy0aDVPbt4SMyQ,33718
3
+ gnosisllm_knowledge/api/knowledge.py,sha256=Hb6kTrZDUwHC1OCmOWLHcAhIHfF96cpJpmYSkK7prv4,34067
4
4
  gnosisllm_knowledge/api/memory.py,sha256=BrYWS3fCPaymwjzlnLqAKiCrUuMlUDMod-DcrTaarkQ,28926
5
5
  gnosisllm_knowledge/backends/__init__.py,sha256=QkVcZbr-u_vDyGiOMs2jrV1oA21VyCLd6g6aJZcSJxk,927
6
6
  gnosisllm_knowledge/backends/memory/__init__.py,sha256=Q_2Fh0-Xs1f7CsNn9V7z8NLW0P7F1owmxZQ4k3fTSXo,238
7
7
  gnosisllm_knowledge/backends/memory/indexer.py,sha256=4BotG-jveyEFjw7F8K-Co-to9NlydvyDA67JncRtD_E,11284
8
- gnosisllm_knowledge/backends/memory/searcher.py,sha256=wCnt_ssfA4YxASjSQ7QnXidTqb0Fk0cYYoqKEuiZBMQ,18591
8
+ gnosisllm_knowledge/backends/memory/searcher.py,sha256=ITdLIxdgw8V789zVQvkBJfRghNsGU9Vz1vnqhRkFzE0,19360
9
9
  gnosisllm_knowledge/backends/opensearch/__init__.py,sha256=wwg3QPVVMLkq5qHUGw_9m6gvXO3hxa-xaBj3_BRJlN4,679
10
10
  gnosisllm_knowledge/backends/opensearch/agentic.py,sha256=VT9ubsgNacgHEIibKtC1mOEAFKV8VjZmf1NhH_exMvk,36861
11
- gnosisllm_knowledge/backends/opensearch/config.py,sha256=9xHMvr5-vnCcGFOPoO2_bbKuB70Gm979BqrhvcKsKm0,8640
11
+ gnosisllm_knowledge/backends/opensearch/config.py,sha256=rA5uojJwlkDZJ-x9-HlO7nm_3SI_zcZ6IJZKQvDS_is,8967
12
12
  gnosisllm_knowledge/backends/opensearch/indexer.py,sha256=0KGagbZ6lMXat9DMSDQgBM-hckNZtDn_eStSlUxQpGo,16803
13
13
  gnosisllm_knowledge/backends/opensearch/mappings.py,sha256=zyUBzSPanGEDMUCDs98X_4uwLqLfRikLIO8ut03oc-w,8279
14
14
  gnosisllm_knowledge/backends/opensearch/memory/__init__.py,sha256=9EganuADfP6-1lky1Mk_oogr2d9pCmOFcNUjO7UMVZo,403
@@ -16,8 +16,8 @@ gnosisllm_knowledge/backends/opensearch/memory/client.py,sha256=ho0Sn1lXK7EDy0RR
16
16
  gnosisllm_knowledge/backends/opensearch/memory/config.py,sha256=astoJqofwhl4KUZ6vTUG-yiG1h5hfpJjYdsBzhKzx_E,4633
17
17
  gnosisllm_knowledge/backends/opensearch/memory/setup.py,sha256=0ILimioesxx2GLQ4I3uCA-Nu9Wy09PI-rSA85t8ssLw,10372
18
18
  gnosisllm_knowledge/backends/opensearch/queries.py,sha256=cubU6fUCeSA6Ag6uDCgrF_pZAj2BgVVmAot2t5sgbxY,13590
19
- gnosisllm_knowledge/backends/opensearch/searcher.py,sha256=3WvvIjjrEr4iZbDoBGdEGsgARJagHEe2CDfJak0_Gbs,20192
20
- gnosisllm_knowledge/backends/opensearch/setup.py,sha256=G9G0KZkz_ZynJjRIRWJImLDEFvQQE2wWRJfq_mlfpIs,59483
19
+ gnosisllm_knowledge/backends/opensearch/searcher.py,sha256=c0lYiyi4g_9Y0MbrY_2OgHi1C5u2M97CwG1YWWb8o9g,22071
20
+ gnosisllm_knowledge/backends/opensearch/setup.py,sha256=7WcykXNfPVI1yidRBf2ZVnshNBrvDAXfAgLoDH7n_6Q,59137
21
21
  gnosisllm_knowledge/chunking/__init__.py,sha256=XKrk04DqwhhPxxP_SLGnQrVUo2fexmvzuEN9YzO4Jxk,225
22
22
  gnosisllm_knowledge/chunking/fixed.py,sha256=LXZhRbYbtg4hQzqrAKyGrM_zmDhqJWXDi6cHOiJlGrM,3905
23
23
  gnosisllm_knowledge/chunking/sentence.py,sha256=EqY2Y1dpfNK_qukXQC9QYrKN8b1dqyyX9TZN5bUYGqs,7271
@@ -52,7 +52,7 @@ gnosisllm_knowledge/core/interfaces/fetcher.py,sha256=19u2TKE6rDjX-8l7xIRKUqa6DL
52
52
  gnosisllm_knowledge/core/interfaces/indexer.py,sha256=PMm2cPJhkVKuNcyLA35S9yYBIk-bBGI2KlQiEv6bKcc,6546
53
53
  gnosisllm_knowledge/core/interfaces/loader.py,sha256=NKq5Yj1Pmbv1I8AUMDtleUCZO5URZFw6kCdx6uLG7Bk,2903
54
54
  gnosisllm_knowledge/core/interfaces/memory.py,sha256=xrgPsqSfmj1-Snmyd35b-HOKipI_9N_FWRQklHj6x8I,13057
55
- gnosisllm_knowledge/core/interfaces/searcher.py,sha256=iuFZ5moiMCRhkWty8171BlpP7Jf-NjYqeArenD-L_4E,5015
55
+ gnosisllm_knowledge/core/interfaces/searcher.py,sha256=5Itba4fHKodLV1eGTL74pYC-9bXwo2-a6-0w6o-CFPc,5512
56
56
  gnosisllm_knowledge/core/interfaces/setup.py,sha256=9DMKVqoNUrWKbS58QCnfwlGk4sD9R5iGe-WLwlW7z-Q,4457
57
57
  gnosisllm_knowledge/core/interfaces/streaming.py,sha256=sQuWTch8gMandFG1AY4xUVbVC-gwijzktq_Pj5QexWc,4160
58
58
  gnosisllm_knowledge/core/streaming/__init__.py,sha256=-PKIWbSu9bI3X3H8bucjuoqBGBiH0UOvM9OSn9dh1Rs,814
@@ -72,10 +72,10 @@ gnosisllm_knowledge/loaders/sitemap_streaming.py,sha256=KkkIg_oJxJrjQP88kS_jvqQv
72
72
  gnosisllm_knowledge/loaders/website.py,sha256=i54S7X0wTYNw9jJQfaoWFqMkGJkbAJeBv-OFBNjPQ_g,1587
73
73
  gnosisllm_knowledge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  gnosisllm_knowledge/services/__init__.py,sha256=qOn1CjsB25UQ2K1TQIGMBe9-CUrGxAKhBrNh9Awo8n8,273
75
- gnosisllm_knowledge/services/indexing.py,sha256=xZDsULtEuQBYyJ8EZvK_Kp-Gpr6oD87DeURtg-CY3A0,13000
76
- gnosisllm_knowledge/services/search.py,sha256=WcoO9LJIqdpmThrv0iKvs0CvNWnB3fBh8kdOKaBLO9I,11992
77
- gnosisllm_knowledge/services/streaming_pipeline.py,sha256=WaLAoDLdgfiBCoqh6A_Im-m4NNwDvCVNtd6f5WrKJPs,11500
78
- gnosisllm_knowledge-0.4.0.dist-info/METADATA,sha256=I1hufeDnDOl_UMdIlPmDgjt_4bv63oDfbmmuQv-grXE,16311
79
- gnosisllm_knowledge-0.4.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
80
- gnosisllm_knowledge-0.4.0.dist-info/entry_points.txt,sha256=-4wtWCTX7bT8WessWJyqfkQ99-2aCVuSoTL1S_QpaOo,68
81
- gnosisllm_knowledge-0.4.0.dist-info/RECORD,,
75
+ gnosisllm_knowledge/services/indexing.py,sha256=MWoe7A_J5bzzSil7rOq363ianBriu3iAuZiy2qnaUKQ,13856
76
+ gnosisllm_knowledge/services/search.py,sha256=ekhD8DrmQ4mmj9pmvzCextFGyn85JWT0HaMviqgi-fo,11869
77
+ gnosisllm_knowledge/services/streaming_pipeline.py,sha256=nztvlgCtK0okHY5Eowk0627a45lts_PkzSTXkeB67xA,11785
78
+ gnosisllm_knowledge-0.4.3.dist-info/METADATA,sha256=iX4v_d9e9aV-n8uo-b8CJSzuMiOm1eiiQlx0btLiQ68,16311
79
+ gnosisllm_knowledge-0.4.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
80
+ gnosisllm_knowledge-0.4.3.dist-info/entry_points.txt,sha256=-4wtWCTX7bT8WessWJyqfkQ99-2aCVuSoTL1S_QpaOo,68
81
+ gnosisllm_knowledge-0.4.3.dist-info/RECORD,,