smartmemory-core 0.5.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.
- scripts/add_license_headers.py +122 -0
- smartmemory/__init__.py +29 -0
- smartmemory/__version__.py +50 -0
- smartmemory/background/__init__.py +0 -0
- smartmemory/background/extraction_worker.py +186 -0
- smartmemory/background/id_resolver.py +72 -0
- smartmemory/clear_all.py +130 -0
- smartmemory/cli.py +164 -0
- smartmemory/clustering/__init__.py +37 -0
- smartmemory/clustering/deduplicator.py +148 -0
- smartmemory/clustering/embedding.py +148 -0
- smartmemory/clustering/global_cluster.py +305 -0
- smartmemory/clustering/graph_aggregator.py +186 -0
- smartmemory/clustering/llm.py +265 -0
- smartmemory/code/__init__.py +16 -0
- smartmemory/code/indexer.py +482 -0
- smartmemory/code/models.py +126 -0
- smartmemory/code/parser.py +404 -0
- smartmemory/code/search.py +145 -0
- smartmemory/code/ts_parser.py +708 -0
- smartmemory/configuration/__init__.py +288 -0
- smartmemory/configuration/environment.py +99 -0
- smartmemory/configuration/loader.py +135 -0
- smartmemory/configuration/manager.py +239 -0
- smartmemory/configuration/models.py +154 -0
- smartmemory/configuration/validator.py +383 -0
- smartmemory/conversation/__init__.py +8 -0
- smartmemory/conversation/context.py +81 -0
- smartmemory/conversation/manager.py +98 -0
- smartmemory/conversation/session.py +37 -0
- smartmemory/decisions/__init__.py +6 -0
- smartmemory/decisions/manager.py +337 -0
- smartmemory/decisions/queries.py +255 -0
- smartmemory/drift_detector.py +135 -0
- smartmemory/evolution/__init__.py +49 -0
- smartmemory/evolution/cycle.py +35 -0
- smartmemory/evolution/diff_engine.py +212 -0
- smartmemory/evolution/flow.py +75 -0
- smartmemory/evolution/models.py +244 -0
- smartmemory/evolution/registry.py +198 -0
- smartmemory/evolution/store.py +323 -0
- smartmemory/evolution/tracker.py +353 -0
- smartmemory/evolution/utilities.py +315 -0
- smartmemory/extraction/__init__.py +0 -0
- smartmemory/extraction/extractor.py +499 -0
- smartmemory/extraction/models.py +87 -0
- smartmemory/feedback/__init__.py +1 -0
- smartmemory/feedback/agentic_improvement.py +79 -0
- smartmemory/feedback/agentic_routines.py +204 -0
- smartmemory/feedback/cli.py +7 -0
- smartmemory/feedback/manager.py +27 -0
- smartmemory/feedback/slack.py +65 -0
- smartmemory/graph/__init__.py +0 -0
- smartmemory/graph/analytics/__init__.py +0 -0
- smartmemory/graph/analytics/edge.py +17 -0
- smartmemory/graph/analytics/episodes.py +28 -0
- smartmemory/graph/analytics/export.py +28 -0
- smartmemory/graph/analytics/prune.py +52 -0
- smartmemory/graph/analytics/temporal.py +30 -0
- smartmemory/graph/backends/__init__.py +11 -0
- smartmemory/graph/backends/async_backend.py +102 -0
- smartmemory/graph/backends/async_falkordb.py +619 -0
- smartmemory/graph/backends/backend.py +96 -0
- smartmemory/graph/backends/falkordb.py +1173 -0
- smartmemory/graph/backends/queries.py +149 -0
- smartmemory/graph/backends/sqlite.py +464 -0
- smartmemory/graph/base.py +138 -0
- smartmemory/graph/core/__init__.py +0 -0
- smartmemory/graph/core/edges.py +252 -0
- smartmemory/graph/core/memory_path.py +274 -0
- smartmemory/graph/core/nodes.py +374 -0
- smartmemory/graph/core/search.py +399 -0
- smartmemory/graph/indexes.py +26 -0
- smartmemory/graph/models/__init__.py +0 -0
- smartmemory/graph/models/canonical_types.py +213 -0
- smartmemory/graph/models/node_types.py +305 -0
- smartmemory/graph/models/schema_validator.py +828 -0
- smartmemory/graph/ontology_graph.py +477 -0
- smartmemory/graph/smartgraph.py +565 -0
- smartmemory/graph/types/__init__.py +0 -0
- smartmemory/graph/types/episodic.py +345 -0
- smartmemory/graph/types/interfaces.py +165 -0
- smartmemory/graph/types/procedural.py +224 -0
- smartmemory/graph/types/semantic.py +342 -0
- smartmemory/graph/types/zettel.py +187 -0
- smartmemory/grounding/__init__.py +15 -0
- smartmemory/grounding/executors.py +40 -0
- smartmemory/grounding/registry.py +24 -0
- smartmemory/grounding/schemas.py +41 -0
- smartmemory/inference/__init__.py +6 -0
- smartmemory/inference/engine.py +131 -0
- smartmemory/inference/rules.py +82 -0
- smartmemory/integration/__init__.py +0 -0
- smartmemory/integration/archive/archive_provider.py +59 -0
- smartmemory/integration/chat/__init__.py +0 -0
- smartmemory/integration/chat/hitl.py +507 -0
- smartmemory/integration/chat/integration.py +274 -0
- smartmemory/integration/llm/__init__.py +0 -0
- smartmemory/integration/llm/llm_client.py +461 -0
- smartmemory/integration/llm/prompts/__init__.py +0 -0
- smartmemory/integration/llm/prompts/prompt_manager.py +275 -0
- smartmemory/integration/llm/prompts/prompt_provider.py +184 -0
- smartmemory/integration/llm/prompts/prompts.json +43 -0
- smartmemory/integration/llm/prompts/prompts_loader.py +204 -0
- smartmemory/integration/llm/providers.py +373 -0
- smartmemory/integration/llm/response_parser.py +258 -0
- smartmemory/integration/wikipedia_client.py +139 -0
- smartmemory/interfaces.py +43 -0
- smartmemory/managers/__init__.py +13 -0
- smartmemory/managers/debug.py +169 -0
- smartmemory/managers/enrichment.py +38 -0
- smartmemory/managers/evolution.py +30 -0
- smartmemory/managers/monitoring.py +40 -0
- smartmemory/memory/__init__.py +0 -0
- smartmemory/memory/base.py +302 -0
- smartmemory/memory/canonical_store.py +67 -0
- smartmemory/memory/context_types.py +14 -0
- smartmemory/memory/ingestion/__init__.py +0 -0
- smartmemory/memory/ingestion/enrichment.py +122 -0
- smartmemory/memory/ingestion/extraction.py +268 -0
- smartmemory/memory/ingestion/flow.py +486 -0
- smartmemory/memory/ingestion/grounding.py +30 -0
- smartmemory/memory/ingestion/observer.py +260 -0
- smartmemory/memory/ingestion/registry.py +250 -0
- smartmemory/memory/ingestion/storage.py +234 -0
- smartmemory/memory/ingestion/utils.py +231 -0
- smartmemory/memory/memory_factory.py +228 -0
- smartmemory/memory/migrate.py +127 -0
- smartmemory/memory/mixins.py +335 -0
- smartmemory/memory/models/__init__.py +0 -0
- smartmemory/memory/models/memory_item.py +64 -0
- smartmemory/memory/pipeline/__init__.py +0 -0
- smartmemory/memory/pipeline/classification.py +176 -0
- smartmemory/memory/pipeline/components.py +534 -0
- smartmemory/memory/pipeline/config.py +249 -0
- smartmemory/memory/pipeline/enrichment.py +295 -0
- smartmemory/memory/pipeline/extractor.py +511 -0
- smartmemory/memory/pipeline/grounding.py +194 -0
- smartmemory/memory/pipeline/input_adapter.py +166 -0
- smartmemory/memory/pipeline/linking.py +394 -0
- smartmemory/memory/pipeline/plugin_base.py +70 -0
- smartmemory/memory/pipeline/stages/__init__.py +7 -0
- smartmemory/memory/pipeline/stages/analytics.py +641 -0
- smartmemory/memory/pipeline/stages/clustering.py +139 -0
- smartmemory/memory/pipeline/stages/coreference.py +303 -0
- smartmemory/memory/pipeline/stages/crud.py +372 -0
- smartmemory/memory/pipeline/stages/enrichment.py +130 -0
- smartmemory/memory/pipeline/stages/evolution.py +272 -0
- smartmemory/memory/pipeline/stages/graph_operations.py +97 -0
- smartmemory/memory/pipeline/stages/grounding.py +95 -0
- smartmemory/memory/pipeline/stages/linking.py +69 -0
- smartmemory/memory/pipeline/stages/monitoring.py +118 -0
- smartmemory/memory/pipeline/stages/personalization.py +29 -0
- smartmemory/memory/pipeline/stages/search.py +208 -0
- smartmemory/memory/pipeline/stages/validation.py +204 -0
- smartmemory/memory/pipeline/state.py +147 -0
- smartmemory/memory/pipeline/storage.py +372 -0
- smartmemory/memory/pipeline/transactions/__init__.py +0 -0
- smartmemory/memory/pipeline/transactions/change_set.py +70 -0
- smartmemory/memory/pipeline/transactions/ops.py +324 -0
- smartmemory/memory/pipeline/transactions/transaction.py +87 -0
- smartmemory/memory/types/__init__.py +0 -0
- smartmemory/memory/types/episodic_memory.py +197 -0
- smartmemory/memory/types/factory_memory_creator.py +268 -0
- smartmemory/memory/types/procedural_memory.py +192 -0
- smartmemory/memory/types/semantic_memory.py +156 -0
- smartmemory/memory/types/wikilink_parser.py +352 -0
- smartmemory/memory/types/working_memory.py +179 -0
- smartmemory/memory/types/zettel_extensions.py +873 -0
- smartmemory/memory/types/zettel_memory.py +460 -0
- smartmemory/memory/utils.py +16 -0
- smartmemory/metrics/__init__.py +5 -0
- smartmemory/metrics/graph_health.py +134 -0
- smartmemory/models/__init__.py +51 -0
- smartmemory/models/base.py +81 -0
- smartmemory/models/compat/__init__.py +0 -0
- smartmemory/models/compat/boundary_converter.py +172 -0
- smartmemory/models/compat/dataclass_model.py +300 -0
- smartmemory/models/compat/simple_boundary.py +62 -0
- smartmemory/models/concept.py +39 -0
- smartmemory/models/conditional_step.py +31 -0
- smartmemory/models/corpus.py +9 -0
- smartmemory/models/decision.py +277 -0
- smartmemory/models/drift_event.py +111 -0
- smartmemory/models/entity.py +37 -0
- smartmemory/models/entity_types.py +70 -0
- smartmemory/models/library.py +41 -0
- smartmemory/models/link_types.py +15 -0
- smartmemory/models/memory_item.py +365 -0
- smartmemory/models/note.py +31 -0
- smartmemory/models/ontology.py +456 -0
- smartmemory/models/opinion.py +228 -0
- smartmemory/models/procedure.py +34 -0
- smartmemory/models/reasoning.py +222 -0
- smartmemory/models/schema_snapshot.py +97 -0
- smartmemory/models/status.py +46 -0
- smartmemory/models/step.py +33 -0
- smartmemory/models/tag.py +36 -0
- smartmemory/models/user_model.py +74 -0
- smartmemory/observability/__init__.py +10 -0
- smartmemory/observability/events.py +587 -0
- smartmemory/observability/instrumentation.py +446 -0
- smartmemory/observability/json_formatter.py +38 -0
- smartmemory/observability/logging_filter.py +117 -0
- smartmemory/observability/retrieval_tracking.py +210 -0
- smartmemory/observability/tracing.py +263 -0
- smartmemory/ontology/__init__.py +0 -0
- smartmemory/ontology/entity_pair_cache.py +94 -0
- smartmemory/ontology/governance.py +499 -0
- smartmemory/ontology/governance_manager.py +125 -0
- smartmemory/ontology/hitl/__init__.py +0 -0
- smartmemory/ontology/hitl/hitl_interface.py +453 -0
- smartmemory/ontology/inference/__init__.py +0 -0
- smartmemory/ontology/inference/inference.py +486 -0
- smartmemory/ontology/inference/ontogpt.py +509 -0
- smartmemory/ontology/ir_models.py +277 -0
- smartmemory/ontology/layered.py +210 -0
- smartmemory/ontology/llm_manager.py +515 -0
- smartmemory/ontology/manager.py +197 -0
- smartmemory/ontology/models.py +354 -0
- smartmemory/ontology/pattern_manager.py +170 -0
- smartmemory/ontology/promotion.py +324 -0
- smartmemory/ontology/reasoning_validator.py +182 -0
- smartmemory/ontology/registry.py +528 -0
- smartmemory/ontology/template_service.py +239 -0
- smartmemory/ontology/templates/_manifest.json +29 -0
- smartmemory/ontology/templates/business.json +312 -0
- smartmemory/ontology/templates/general.json +275 -0
- smartmemory/ontology/templates/software.json +337 -0
- smartmemory/pipeline/__init__.py +24 -0
- smartmemory/pipeline/config.py +355 -0
- smartmemory/pipeline/metrics_consumer.py +369 -0
- smartmemory/pipeline/protocol.py +38 -0
- smartmemory/pipeline/runner.py +259 -0
- smartmemory/pipeline/stages/__init__.py +31 -0
- smartmemory/pipeline/stages/classify.py +75 -0
- smartmemory/pipeline/stages/coreference.py +61 -0
- smartmemory/pipeline/stages/enrich.py +93 -0
- smartmemory/pipeline/stages/entity_ruler.py +178 -0
- smartmemory/pipeline/stages/evolve.py +53 -0
- smartmemory/pipeline/stages/ground.py +119 -0
- smartmemory/pipeline/stages/link.py +54 -0
- smartmemory/pipeline/stages/llm_extract.py +162 -0
- smartmemory/pipeline/stages/ontology_constrain.py +392 -0
- smartmemory/pipeline/stages/reasoning_detect.py +63 -0
- smartmemory/pipeline/stages/simplify.py +149 -0
- smartmemory/pipeline/stages/store.py +212 -0
- smartmemory/pipeline/state.py +138 -0
- smartmemory/pipeline/token_tracker.py +185 -0
- smartmemory/pipeline/transport/__init__.py +30 -0
- smartmemory/pipeline/transport/event_bus.py +364 -0
- smartmemory/plugins/__init__.py +51 -0
- smartmemory/plugins/base.py +445 -0
- smartmemory/plugins/embedding.py +232 -0
- smartmemory/plugins/enrichers/__init__.py +16 -0
- smartmemory/plugins/enrichers/basic.py +76 -0
- smartmemory/plugins/enrichers/link_expansion.py +540 -0
- smartmemory/plugins/enrichers/sentiment.py +157 -0
- smartmemory/plugins/enrichers/skills_tools.py +70 -0
- smartmemory/plugins/enrichers/temporal.py +97 -0
- smartmemory/plugins/enrichers/topic.py +137 -0
- smartmemory/plugins/enrichers/usage_tracking.py +122 -0
- smartmemory/plugins/evolvers/__init__.py +59 -0
- smartmemory/plugins/evolvers/base.py +11 -0
- smartmemory/plugins/evolvers/decision_confidence.py +322 -0
- smartmemory/plugins/evolvers/enhanced/__init__.py +71 -0
- smartmemory/plugins/evolvers/enhanced/exponential_decay.py +129 -0
- smartmemory/plugins/evolvers/enhanced/hebbian_co_retrieval.py +128 -0
- smartmemory/plugins/evolvers/enhanced/interference_based_consolidation.py +173 -0
- smartmemory/plugins/evolvers/enhanced/retrieval_based_strengthening.py +215 -0
- smartmemory/plugins/evolvers/enhanced/working_to_episodic.py +193 -0
- smartmemory/plugins/evolvers/episodic_decay.py +53 -0
- smartmemory/plugins/evolvers/episodic_to_semantic.py +57 -0
- smartmemory/plugins/evolvers/episodic_to_zettel.py +54 -0
- smartmemory/plugins/evolvers/observation_synthesis.py +333 -0
- smartmemory/plugins/evolvers/opinion_reinforcement.py +270 -0
- smartmemory/plugins/evolvers/opinion_synthesis.py +282 -0
- smartmemory/plugins/evolvers/semantic_decay.py +53 -0
- smartmemory/plugins/evolvers/working_to_episodic.py +65 -0
- smartmemory/plugins/evolvers/working_to_procedural.py +328 -0
- smartmemory/plugins/evolvers/zettel_prune.py +113 -0
- smartmemory/plugins/executor.py +150 -0
- smartmemory/plugins/extractors/__init__.py +13 -0
- smartmemory/plugins/extractors/conversation_aware_llm.py +546 -0
- smartmemory/plugins/extractors/decision.py +192 -0
- smartmemory/plugins/extractors/llm.py +319 -0
- smartmemory/plugins/extractors/llm_single.py +521 -0
- smartmemory/plugins/extractors/reasoning.py +440 -0
- smartmemory/plugins/extractors/spacy.py +216 -0
- smartmemory/plugins/grounders/__init__.py +5 -0
- smartmemory/plugins/grounders/wikipedia.py +126 -0
- smartmemory/plugins/manager.py +615 -0
- smartmemory/plugins/registry.py +499 -0
- smartmemory/plugins/resolvers/__init__.py +0 -0
- smartmemory/plugins/resolvers/external_resolver.py +57 -0
- smartmemory/plugins/security.py +391 -0
- smartmemory/procedure_matcher.py +188 -0
- smartmemory/procedures/__init__.py +45 -0
- smartmemory/procedures/candidate_namer.py +354 -0
- smartmemory/procedures/candidate_scorer.py +239 -0
- smartmemory/procedures/models.py +104 -0
- smartmemory/procedures/pattern_detector.py +273 -0
- smartmemory/py.typed +0 -0
- smartmemory/reasoning/__init__.py +37 -0
- smartmemory/reasoning/challenger.py +421 -0
- smartmemory/reasoning/confidence.py +104 -0
- smartmemory/reasoning/detection/__init__.py +18 -0
- smartmemory/reasoning/detection/base.py +31 -0
- smartmemory/reasoning/detection/cascade.py +25 -0
- smartmemory/reasoning/detection/embedding.py +71 -0
- smartmemory/reasoning/detection/graph.py +86 -0
- smartmemory/reasoning/detection/heuristic.py +97 -0
- smartmemory/reasoning/detection/llm.py +71 -0
- smartmemory/reasoning/fuzzy_confidence.py +119 -0
- smartmemory/reasoning/models.py +84 -0
- smartmemory/reasoning/proof_tree.py +154 -0
- smartmemory/reasoning/query_router.py +138 -0
- smartmemory/reasoning/reasoner.py +50 -0
- smartmemory/reasoning/residuation.py +147 -0
- smartmemory/reasoning/resolution/__init__.py +17 -0
- smartmemory/reasoning/resolution/base.py +19 -0
- smartmemory/reasoning/resolution/cascade.py +24 -0
- smartmemory/reasoning/resolution/grounding.py +51 -0
- smartmemory/reasoning/resolution/llm.py +75 -0
- smartmemory/reasoning/resolution/recency.py +41 -0
- smartmemory/reasoning/resolution/wikipedia.py +101 -0
- smartmemory/retrieval/__init__.py +14 -0
- smartmemory/retrieval/ssg_traversal.py +595 -0
- smartmemory/schema_diff.py +355 -0
- smartmemory/schema_providers.py +95 -0
- smartmemory/scope_provider.py +70 -0
- smartmemory/similarity/__init__.py +0 -0
- smartmemory/similarity/enhanced_metrics.py +849 -0
- smartmemory/similarity/framework.py +448 -0
- smartmemory/smart_memory.py +1539 -0
- smartmemory/stores/__init__.py +1 -0
- smartmemory/stores/base.py +228 -0
- smartmemory/stores/converters/__init__.py +0 -0
- smartmemory/stores/converters/episodic_converter.py +350 -0
- smartmemory/stores/converters/procedural_converter.py +440 -0
- smartmemory/stores/converters/semantic_converter.py +290 -0
- smartmemory/stores/converters/zettel_converter.py +341 -0
- smartmemory/stores/corpus/__init__.py +0 -0
- smartmemory/stores/corpus/store.py +76 -0
- smartmemory/stores/example_memory_handler.py +235 -0
- smartmemory/stores/external/__init__.py +0 -0
- smartmemory/stores/external/file_handler.py +180 -0
- smartmemory/stores/external/mcp_handler.py +143 -0
- smartmemory/stores/external/s3_handler.py +101 -0
- smartmemory/stores/external/web_handler.py +75 -0
- smartmemory/stores/factory.py +265 -0
- smartmemory/stores/json_store.py +363 -0
- smartmemory/stores/mixins.py +354 -0
- smartmemory/stores/ontology/__init__.py +94 -0
- smartmemory/stores/ontology/falkordb.py +360 -0
- smartmemory/stores/ontology/redis_service.py +336 -0
- smartmemory/stores/persistence/__init__.py +20 -0
- smartmemory/stores/persistence/base.py +56 -0
- smartmemory/stores/persistence/entity_handler.py +21 -0
- smartmemory/stores/persistence/json.py +125 -0
- smartmemory/stores/persistence/model.py +131 -0
- smartmemory/stores/persistence/ontology_handlers.py +175 -0
- smartmemory/stores/registrations.py +6 -0
- smartmemory/stores/registry.py +34 -0
- smartmemory/stores/vector/__init__.py +0 -0
- smartmemory/stores/vector/backends/__init__.py +7 -0
- smartmemory/stores/vector/backends/base.py +72 -0
- smartmemory/stores/vector/backends/falkor.py +219 -0
- smartmemory/stores/vector/backends/usearch.py +239 -0
- smartmemory/stores/vector/vector_store.py +390 -0
- smartmemory/streams/__init__.py +1 -0
- smartmemory/streams/pipeline_producer.py +110 -0
- smartmemory/temporal/__init__.py +41 -0
- smartmemory/temporal/context.py +84 -0
- smartmemory/temporal/performance.py +429 -0
- smartmemory/temporal/queries.py +746 -0
- smartmemory/temporal/relationships.py +538 -0
- smartmemory/temporal/version_tracker.py +478 -0
- smartmemory/tools/__init__.py +1 -0
- smartmemory/tools/factory.py +120 -0
- smartmemory/tools/markdown_writer.py +44 -0
- smartmemory/utils/__init__.py +120 -0
- smartmemory/utils/cache.py +286 -0
- smartmemory/utils/chunking.py +648 -0
- smartmemory/utils/context.py +47 -0
- smartmemory/utils/deduplication.py +427 -0
- smartmemory/utils/hybrid_retrieval.py +323 -0
- smartmemory/utils/llm.py +203 -0
- smartmemory/utils/llm_client/dspy.py +283 -0
- smartmemory/utils/llm_client/litellm.py +91 -0
- smartmemory/utils/llm_client/openai.py +76 -0
- smartmemory/utils/pipeline_utils.py +67 -0
- smartmemory/utils/serialization.py +188 -0
- smartmemory/utils/token_tracking.py +287 -0
- smartmemory/validation/__init__.py +6 -0
- smartmemory/validation/edge_validator.py +135 -0
- smartmemory/validation/memory_validator.py +148 -0
- smartmemory_core-0.5.0.dist-info/METADATA +768 -0
- smartmemory_core-0.5.0.dist-info/RECORD +405 -0
- smartmemory_core-0.5.0.dist-info/WHEEL +5 -0
- smartmemory_core-0.5.0.dist-info/entry_points.txt +2 -0
- smartmemory_core-0.5.0.dist-info/licenses/LICENSE +31 -0
- smartmemory_core-0.5.0.dist-info/licenses/LICENSE.agpl-v3 +661 -0
- smartmemory_core-0.5.0.dist-info/licenses/LICENSE.header +18 -0
- smartmemory_core-0.5.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Script to add AGPL v3 license headers with dual licensing notice to all Python files.
|
|
4
|
+
Run this from the project root directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
LICENSE_HEADER = '''# Copyright (C) 2025 SmartMemory
|
|
11
|
+
#
|
|
12
|
+
# This program is free software: you can redistribute it and/or modify
|
|
13
|
+
# it under the terms of the GNU Affero General Public License as published by
|
|
14
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
15
|
+
# (at your option) any later version.
|
|
16
|
+
#
|
|
17
|
+
# This program is distributed in the hope that it will be useful,
|
|
18
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20
|
+
# GNU Affero General Public License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# For commercial licensing options, please contact: help@smartmemory.ai
|
|
26
|
+
# Commercial licenses are available for organizations that wish to use
|
|
27
|
+
# this software in proprietary applications without the AGPL restrictions.
|
|
28
|
+
|
|
29
|
+
'''
|
|
30
|
+
|
|
31
|
+
def find_python_files(directory: str) -> List[str]:
|
|
32
|
+
"""Find all Python files in directory and subdirectories."""
|
|
33
|
+
python_files = []
|
|
34
|
+
for root, dirs, files in os.walk(directory):
|
|
35
|
+
# Skip hidden directories and common build/cache directories
|
|
36
|
+
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['__pycache__', 'build', 'dist']]
|
|
37
|
+
|
|
38
|
+
for file in files:
|
|
39
|
+
if file.endswith('.py'):
|
|
40
|
+
python_files.append(os.path.join(root, file))
|
|
41
|
+
|
|
42
|
+
return python_files
|
|
43
|
+
|
|
44
|
+
def has_license_header(file_path: str) -> bool:
|
|
45
|
+
"""Check if file already has a license header."""
|
|
46
|
+
try:
|
|
47
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
48
|
+
content = f.read(1000) # Check first 1000 characters
|
|
49
|
+
return 'Copyright (C) 2025 SmartMemory' in content or 'GNU Affero General Public License' in content
|
|
50
|
+
except Exception:
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def add_license_header(file_path: str) -> bool:
|
|
54
|
+
"""Add license header to Python file."""
|
|
55
|
+
try:
|
|
56
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
57
|
+
original_content = f.read()
|
|
58
|
+
|
|
59
|
+
# Handle shebang line
|
|
60
|
+
lines = original_content.split('\n')
|
|
61
|
+
if lines and lines[0].startswith('#!'):
|
|
62
|
+
# Keep shebang, add license after it
|
|
63
|
+
shebang = lines[0] + '\n'
|
|
64
|
+
rest_content = '\n'.join(lines[1:])
|
|
65
|
+
new_content = shebang + LICENSE_HEADER + rest_content
|
|
66
|
+
else:
|
|
67
|
+
# Add license at the beginning
|
|
68
|
+
new_content = LICENSE_HEADER + original_content
|
|
69
|
+
|
|
70
|
+
# Write back the file
|
|
71
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
72
|
+
f.write(new_content)
|
|
73
|
+
|
|
74
|
+
return True
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(f"Error processing {file_path}: {e}")
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
def main():
|
|
80
|
+
"""Main function to process all Python files."""
|
|
81
|
+
project_root = os.path.dirname(os.path.abspath(__file__))
|
|
82
|
+
|
|
83
|
+
# Find all Python files
|
|
84
|
+
python_files = find_python_files(project_root)
|
|
85
|
+
|
|
86
|
+
# Filter out this script itself and other utility scripts you might not want to modify
|
|
87
|
+
exclude_files = [
|
|
88
|
+
os.path.join(project_root, 'add_license_headers.py'),
|
|
89
|
+
# Add other files to exclude here if needed
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
python_files = [f for f in python_files if f not in exclude_files]
|
|
93
|
+
|
|
94
|
+
print(f"Found {len(python_files)} Python files")
|
|
95
|
+
|
|
96
|
+
processed = 0
|
|
97
|
+
skipped = 0
|
|
98
|
+
errors = 0
|
|
99
|
+
|
|
100
|
+
for file_path in python_files:
|
|
101
|
+
relative_path = os.path.relpath(file_path, project_root)
|
|
102
|
+
|
|
103
|
+
if has_license_header(file_path):
|
|
104
|
+
print(f"SKIP: {relative_path} (already has license header)")
|
|
105
|
+
skipped += 1
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
if add_license_header(file_path):
|
|
109
|
+
print(f"ADD: {relative_path}")
|
|
110
|
+
processed += 1
|
|
111
|
+
else:
|
|
112
|
+
print(f"ERR: {relative_path}")
|
|
113
|
+
errors += 1
|
|
114
|
+
|
|
115
|
+
print("\nSummary:")
|
|
116
|
+
print(f" Processed: {processed}")
|
|
117
|
+
print(f" Skipped: {skipped}")
|
|
118
|
+
print(f" Errors: {errors}")
|
|
119
|
+
print(f" Total files: {len(python_files)}")
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
main()
|
smartmemory/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SmartMemory: Multi-layered AI memory system with graph databases, vector stores, and
|
|
3
|
+
intelligent processing pipelines for context-aware AI applications. Provides semantic,
|
|
4
|
+
episodic, procedural, and working memory types with advanced relationship modeling and
|
|
5
|
+
storage and retrieval for AI applications.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from smartmemory.__version__ import __version__, __version_info__
|
|
9
|
+
|
|
10
|
+
__author__ = "SmartMemory Team"
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"SmartMemory",
|
|
14
|
+
"MemoryItem",
|
|
15
|
+
"__version__",
|
|
16
|
+
"__version_info__",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def __getattr__(name: str):
|
|
21
|
+
"""Lazy-load heavy top-level exports so submodule imports don't pull the
|
|
22
|
+
full ML stack (torch, numpy, etc.) unless SmartMemory itself is used."""
|
|
23
|
+
if name == "SmartMemory":
|
|
24
|
+
from smartmemory.smart_memory import SmartMemory
|
|
25
|
+
return SmartMemory
|
|
26
|
+
if name == "MemoryItem":
|
|
27
|
+
from smartmemory.models.memory_item import MemoryItem
|
|
28
|
+
return MemoryItem
|
|
29
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SmartMemory version information.
|
|
3
|
+
|
|
4
|
+
Version is read dynamically from package metadata or VERSION file.
|
|
5
|
+
Single source of truth: VERSION file
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
def _get_version() -> str:
|
|
11
|
+
"""Get version from VERSION file or package metadata."""
|
|
12
|
+
try:
|
|
13
|
+
# For installed package, read from metadata
|
|
14
|
+
from importlib.metadata import version
|
|
15
|
+
return version("smartmemory-core")
|
|
16
|
+
except Exception:
|
|
17
|
+
# For development, read from VERSION file
|
|
18
|
+
try:
|
|
19
|
+
version_file = Path(__file__).parent.parent / "VERSION"
|
|
20
|
+
with open(version_file, "r", encoding="utf-8") as f:
|
|
21
|
+
return f.read().strip()
|
|
22
|
+
except FileNotFoundError:
|
|
23
|
+
# Fallback if VERSION file not found
|
|
24
|
+
return "unknown"
|
|
25
|
+
|
|
26
|
+
__version__ = _get_version()
|
|
27
|
+
|
|
28
|
+
# Parse version info
|
|
29
|
+
try:
|
|
30
|
+
__version_info__ = tuple(int(x) for x in __version__.split("."))
|
|
31
|
+
except Exception:
|
|
32
|
+
__version_info__ = (0, 1, 6)
|
|
33
|
+
|
|
34
|
+
# Version history:
|
|
35
|
+
# 0.3.0 - Decision Memory: Decision model, DecisionManager, DecisionQueries, DecisionExtractor, edge schemas (PRODUCED, DERIVED_FROM, SUPERSEDES, CONTRADICTS, INFLUENCES)
|
|
36
|
+
# 0.2.6 - SSG integration, user signature support, documentation updates, DRY version management (VERSION file as single source of truth)
|
|
37
|
+
# 0.1.14 - Tenant isolation bug fix
|
|
38
|
+
# 0.1.13 - Tenant isolation bug fix
|
|
39
|
+
# 0.1.12 - Tenant isolation improvements
|
|
40
|
+
# 0.1.11 - Version bump and maintenance
|
|
41
|
+
# 0.1.10 - Minor fix to grounder plugin, publish script improvements
|
|
42
|
+
# 0.1.9 - Add dspy to requirements
|
|
43
|
+
# 0.1.8 - README overhaul: fixed all code snippets to use public API, verified evolvers, added "In Progress" section, removed internal imports
|
|
44
|
+
# 0.1.7 - Updated README, removed ChromaDB references, fixed PyPI deployment
|
|
45
|
+
# 0.1.6 - Production PyPI deployment setup
|
|
46
|
+
# 0.1.5 - Complete bi-temporal implementation: version tracking, temporal search, relationship queries, bi-temporal joins, performance optimizations
|
|
47
|
+
# 0.1.4 - Bi-temporal queries: time-travel, audit trails, version history, rollback
|
|
48
|
+
# 0.1.3 - Zettelkasten system with wikilink support, documentation, examples, CLI
|
|
49
|
+
# 0.1.2 - ChromaDB optional, Python 3.10+ requirement, version externalized
|
|
50
|
+
# 0.1.1 - Plugin system with security
|
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Tier 2 async extraction worker — runs LLM extraction on items stored by Tier 1.
|
|
2
|
+
|
|
3
|
+
Tier 1 (sync, ~4ms): spaCy + EntityRuler → stores memory, enqueues item_id.
|
|
4
|
+
Tier 2 (this module, ~740ms): loads item, runs LLMSingleExtractor, diffs against
|
|
5
|
+
ruler entities already stored, writes net-new entities + resolved relations,
|
|
6
|
+
then updates EntityPattern nodes for the self-learning EntityRuler loop.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, Optional, Set
|
|
12
|
+
|
|
13
|
+
from smartmemory.models.memory_item import MemoryItem
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# Pre-write quality gates for entity pattern candidates
|
|
18
|
+
_MIN_CONFIDENCE = 0.8
|
|
19
|
+
_MIN_NAME_LENGTH = 3
|
|
20
|
+
_BLOCKLIST: Set[str] = {"it", "this", "that", "the", "a", "an", "he", "she", "they", "we"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def process_extract_job(
|
|
24
|
+
memory: Any,
|
|
25
|
+
payload: Dict[str, Any],
|
|
26
|
+
redis_client: Optional[Any] = None,
|
|
27
|
+
) -> Dict[str, Any]:
|
|
28
|
+
"""Run Tier 2 LLM extraction on a stored memory item.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
memory: A SmartMemory (or SecureSmartMemory) instance scoped to the workspace.
|
|
32
|
+
payload: Dict from the extract queue: {item_id, workspace_id, entity_ids}.
|
|
33
|
+
entity_ids is {name.lower(): graph_node_id} from Tier 1 StoreStage.
|
|
34
|
+
redis_client: Optional Redis connection for ruler hot-reload pub/sub.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
{"status": "ok"|"no_text"|"item_not_found"|"llm_failed",
|
|
38
|
+
"new_entities": int, "new_relations": int, "new_patterns": int}
|
|
39
|
+
"""
|
|
40
|
+
item_id: Optional[str] = payload.get("item_id")
|
|
41
|
+
workspace_id: str = payload.get("workspace_id", "")
|
|
42
|
+
# ruler_entity_ids: {name.lower(): graph_node_id} populated by Tier 1 StoreStage
|
|
43
|
+
ruler_entity_ids: Dict[str, str] = payload.get("entity_ids") or {}
|
|
44
|
+
# Respect enable_ontology flag set by the enqueuing SmartMemory instance.
|
|
45
|
+
# When False, skip EntityPattern writes and ruler hot-reload entirely.
|
|
46
|
+
enable_ontology: bool = payload.get("enable_ontology", True)
|
|
47
|
+
|
|
48
|
+
# --- 1. Load stored item ---
|
|
49
|
+
item = memory.get(item_id)
|
|
50
|
+
if item is None:
|
|
51
|
+
logger.warning("Extract job: item_id=%s not found", item_id)
|
|
52
|
+
return {"status": "item_not_found", "new_entities": 0, "new_relations": 0, "new_patterns": 0}
|
|
53
|
+
|
|
54
|
+
content: str = ""
|
|
55
|
+
if isinstance(item, MemoryItem):
|
|
56
|
+
content = item.content or ""
|
|
57
|
+
elif isinstance(item, dict):
|
|
58
|
+
content = item.get("content", "") or ""
|
|
59
|
+
|
|
60
|
+
if not content.strip():
|
|
61
|
+
return {"status": "no_text", "new_entities": 0, "new_relations": 0, "new_patterns": 0}
|
|
62
|
+
|
|
63
|
+
# --- 2. Run LLM extraction ---
|
|
64
|
+
try:
|
|
65
|
+
from smartmemory.plugins.extractors.llm_single import LLMSingleExtractor
|
|
66
|
+
|
|
67
|
+
extractor = LLMSingleExtractor()
|
|
68
|
+
extraction = extractor.extract(content)
|
|
69
|
+
except Exception as exc:
|
|
70
|
+
logger.warning("Extract job: LLM extraction failed for item_id=%s: %s", item_id, exc)
|
|
71
|
+
return {"status": "llm_failed", "new_entities": 0, "new_relations": 0, "new_patterns": 0}
|
|
72
|
+
|
|
73
|
+
llm_entities: list[MemoryItem] = extraction.get("entities") or []
|
|
74
|
+
llm_relations: list[dict] = extraction.get("relations") or []
|
|
75
|
+
|
|
76
|
+
# --- 3. Build id remapping and filter valid relations ---
|
|
77
|
+
from smartmemory.background.id_resolver import build_sha256_to_stored, filter_valid_relations
|
|
78
|
+
|
|
79
|
+
sha256_to_stored = build_sha256_to_stored(llm_entities, ruler_entity_ids)
|
|
80
|
+
|
|
81
|
+
# --- 4. Write net-new entities (those NOT already in ruler_entity_ids) ---
|
|
82
|
+
net_new_ids: Set[str] = set()
|
|
83
|
+
new_entity_count = 0
|
|
84
|
+
ruler_names_lower = {n.lower() for n in ruler_entity_ids}
|
|
85
|
+
|
|
86
|
+
for entity in llm_entities:
|
|
87
|
+
name = (entity.metadata.get("name") or "").strip()
|
|
88
|
+
if not name or name.lower() in ruler_names_lower:
|
|
89
|
+
continue
|
|
90
|
+
# Net-new entity — write to graph via simple add()
|
|
91
|
+
try:
|
|
92
|
+
result = memory._crud.add(entity)
|
|
93
|
+
if isinstance(result, dict):
|
|
94
|
+
net_new_node_id = result.get("memory_node_id") or entity.item_id
|
|
95
|
+
else:
|
|
96
|
+
net_new_node_id = result or entity.item_id
|
|
97
|
+
if net_new_node_id:
|
|
98
|
+
net_new_ids.add(net_new_node_id)
|
|
99
|
+
# Update sha256→stored so relations can resolve this entity too
|
|
100
|
+
if entity.item_id:
|
|
101
|
+
sha256_to_stored[entity.item_id] = net_new_node_id
|
|
102
|
+
new_entity_count += 1
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
logger.warning("Extract job: failed to write net-new entity '%s': %s", name, exc)
|
|
105
|
+
|
|
106
|
+
# --- 5. Write resolved relations ---
|
|
107
|
+
resolved_relations = filter_valid_relations(llm_relations, sha256_to_stored, net_new_ids)
|
|
108
|
+
new_relation_count = 0
|
|
109
|
+
for rel in resolved_relations:
|
|
110
|
+
try:
|
|
111
|
+
memory._graph.add_edge(
|
|
112
|
+
rel["source_id"],
|
|
113
|
+
rel["target_id"],
|
|
114
|
+
edge_type=rel["relation_type"],
|
|
115
|
+
properties={},
|
|
116
|
+
)
|
|
117
|
+
new_relation_count += 1
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
logger.warning(
|
|
120
|
+
"Extract job: failed to write relation %s->%s (%s): %s",
|
|
121
|
+
rel["source_id"],
|
|
122
|
+
rel["target_id"],
|
|
123
|
+
rel["relation_type"],
|
|
124
|
+
exc,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# --- 6+7. Update EntityPattern nodes + notify ruler hot-reload ---
|
|
128
|
+
# Guarded by enable_ontology: when False (e.g. SmartMemory(enable_ontology=False)),
|
|
129
|
+
# skip all ontology graph writes and Redis pub/sub entirely.
|
|
130
|
+
new_pattern_count = 0
|
|
131
|
+
if enable_ontology:
|
|
132
|
+
try:
|
|
133
|
+
from smartmemory.graph.ontology_graph import OntologyGraph
|
|
134
|
+
|
|
135
|
+
ontology_graph = OntologyGraph(workspace_id=workspace_id)
|
|
136
|
+
for entity in llm_entities:
|
|
137
|
+
name = (entity.metadata.get("name") or "").strip()
|
|
138
|
+
entity_type = entity.metadata.get("entity_type") or entity.memory_type or "concept"
|
|
139
|
+
confidence = float(entity.metadata.get("confidence", 0.0))
|
|
140
|
+
|
|
141
|
+
# Pre-write quality gates (confidence, length, blocklist)
|
|
142
|
+
if not name or len(name) < _MIN_NAME_LENGTH:
|
|
143
|
+
continue
|
|
144
|
+
if name.lower() in _BLOCKLIST:
|
|
145
|
+
continue
|
|
146
|
+
if confidence < _MIN_CONFIDENCE:
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
added = ontology_graph.add_entity_pattern(
|
|
150
|
+
name=name,
|
|
151
|
+
label=entity_type,
|
|
152
|
+
confidence=confidence,
|
|
153
|
+
workspace_id=workspace_id,
|
|
154
|
+
is_global=False,
|
|
155
|
+
source="llm_discovery",
|
|
156
|
+
)
|
|
157
|
+
if added:
|
|
158
|
+
new_pattern_count += 1
|
|
159
|
+
except Exception as exc:
|
|
160
|
+
logger.warning("Extract job: failed to update entity patterns for item_id=%s: %s", item_id, exc)
|
|
161
|
+
|
|
162
|
+
if new_pattern_count > 0 and redis_client is not None:
|
|
163
|
+
try:
|
|
164
|
+
from smartmemory.ontology.pattern_manager import PatternManager
|
|
165
|
+
|
|
166
|
+
channel = f"{PatternManager.RELOAD_CHANNEL_PREFIX}:{workspace_id}"
|
|
167
|
+
redis_client.publish(channel, "reload")
|
|
168
|
+
logger.debug("Extract job: published ruler reload on %s", channel)
|
|
169
|
+
except Exception as exc:
|
|
170
|
+
logger.warning("Extract job: failed to publish ruler reload: %s", exc)
|
|
171
|
+
else:
|
|
172
|
+
logger.debug("Extract job: ontology disabled; skipping EntityPattern writes for item_id=%s", item_id)
|
|
173
|
+
|
|
174
|
+
logger.info(
|
|
175
|
+
"Extract job ok: item_id=%s new_entities=%d new_relations=%d new_patterns=%d",
|
|
176
|
+
item_id,
|
|
177
|
+
new_entity_count,
|
|
178
|
+
new_relation_count,
|
|
179
|
+
new_pattern_count,
|
|
180
|
+
)
|
|
181
|
+
return {
|
|
182
|
+
"status": "ok",
|
|
183
|
+
"new_entities": new_entity_count,
|
|
184
|
+
"new_relations": new_relation_count,
|
|
185
|
+
"new_patterns": new_pattern_count,
|
|
186
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Id resolution between LLMSingleExtractor's SHA-256 id space and FalkorDB stored ids.
|
|
2
|
+
|
|
3
|
+
LLMSingleExtractor._process_entities() assigns SHA-256 item_ids (hashlib.sha256 of
|
|
4
|
+
"name|entity_type", first 16 chars) to MemoryItem objects. StoreStage._map_entity_ids()
|
|
5
|
+
assigns FalkorDB graph node ids, stored in PipelineState.entity_ids as {name -> graph_id}.
|
|
6
|
+
|
|
7
|
+
Relations from the LLM call reference SHA-256 ids which must be remapped to graph ids
|
|
8
|
+
before writing edges. Both id spaces are needed: sha256_to_stored for ruler entities
|
|
9
|
+
present in both spaces, net_new_ids for entities added only in this Tier 2 run.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from smartmemory.models.memory_item import MemoryItem
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_sha256_to_stored(
|
|
17
|
+
llm_entities: list[MemoryItem],
|
|
18
|
+
ruler_entity_ids: dict[str, str],
|
|
19
|
+
) -> dict[str, str]:
|
|
20
|
+
"""Map SHA-256 ids (LLM extractor space) to stored FalkorDB ids (Tier 1 space).
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
llm_entities: List[MemoryItem] from LLMSingleExtractor.extract()["entities"].
|
|
24
|
+
Accessed via entity.item_id (SHA-256) and entity.metadata["name"].
|
|
25
|
+
ruler_entity_ids: state.entity_ids from the job payload — {name.lower(): graph_node_id}.
|
|
26
|
+
Populated by StoreStage._map_entity_ids() during Tier 1 ingest.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
{sha256_id: graph_node_id} for entities present in both id spaces.
|
|
30
|
+
"""
|
|
31
|
+
remap: dict[str, str] = {}
|
|
32
|
+
for entity in llm_entities:
|
|
33
|
+
name = entity.metadata.get("name", "").lower()
|
|
34
|
+
sha256_id = entity.item_id
|
|
35
|
+
if name and sha256_id and name in ruler_entity_ids:
|
|
36
|
+
stored_id = ruler_entity_ids[name]
|
|
37
|
+
if stored_id:
|
|
38
|
+
remap[sha256_id] = stored_id
|
|
39
|
+
return remap
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def filter_valid_relations(
|
|
43
|
+
llm_relations: list[dict],
|
|
44
|
+
sha256_to_stored: dict[str, str],
|
|
45
|
+
net_new_ids: set[str],
|
|
46
|
+
) -> list[dict]:
|
|
47
|
+
"""Return relations where both endpoints resolve to known graph nodes.
|
|
48
|
+
|
|
49
|
+
An endpoint is valid if it appears in sha256_to_stored (ruler entity remapped
|
|
50
|
+
to a stored id) or in net_new_ids (entity written in this Tier 2 run).
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
llm_relations: List of relation dicts from LLMSingleExtractor.extract()["relations"].
|
|
54
|
+
Each dict: {source_id: sha256, target_id: sha256, relation_type: str}.
|
|
55
|
+
sha256_to_stored: Output of build_sha256_to_stored().
|
|
56
|
+
net_new_ids: Set of item_ids for net-new entities written in this Tier 2 run.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of relation dicts with source_id/target_id remapped to graph node ids.
|
|
60
|
+
"""
|
|
61
|
+
valid_ids = set(sha256_to_stored.values()) | net_new_ids
|
|
62
|
+
resolved = []
|
|
63
|
+
for rel in llm_relations:
|
|
64
|
+
source_id = sha256_to_stored.get(rel["source_id"], rel["source_id"])
|
|
65
|
+
target_id = sha256_to_stored.get(rel["target_id"], rel["target_id"])
|
|
66
|
+
if source_id in valid_ids and target_id in valid_ids:
|
|
67
|
+
resolved.append({
|
|
68
|
+
"source_id": source_id,
|
|
69
|
+
"target_id": target_id,
|
|
70
|
+
"relation_type": rel["relation_type"],
|
|
71
|
+
})
|
|
72
|
+
return resolved
|
smartmemory/clear_all.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility to clear SmartMemory state across caches and stores.
|
|
3
|
+
|
|
4
|
+
Actions:
|
|
5
|
+
- Clear SmartGraph (drops all nodes/edges, clears graph caches)
|
|
6
|
+
- Clear VectorStore (if supported by current backend)
|
|
7
|
+
- Clear Redis-backed extraction caches (best effort)
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python -m smartmemory.maintenance.clear_all --all
|
|
11
|
+
python -m smartmemory.maintenance.clear_all --graph
|
|
12
|
+
python -m smartmemory.maintenance.clear_all --vector
|
|
13
|
+
python -m smartmemory.maintenance.clear_all --cache
|
|
14
|
+
|
|
15
|
+
Notes:
|
|
16
|
+
- This is intended for local/dev use. Use with caution in shared environments.
|
|
17
|
+
- We do not swallow exceptions silently: failures are logged and surfaced.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import logging
|
|
23
|
+
import os
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _setup_logging(verbose: bool):
|
|
28
|
+
level = logging.DEBUG if verbose else logging.INFO
|
|
29
|
+
logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def clear_graph() -> bool:
|
|
33
|
+
from smartmemory.graph.smartgraph import SmartGraph
|
|
34
|
+
|
|
35
|
+
logging.info("Clearing SmartGraph (nodes/edges/caches)...")
|
|
36
|
+
try:
|
|
37
|
+
g = SmartGraph()
|
|
38
|
+
ok = g.clear()
|
|
39
|
+
logging.info("SmartGraph cleared: %s", ok)
|
|
40
|
+
return bool(ok)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
logging.error("Failed to clear SmartGraph: %s", e)
|
|
43
|
+
raise
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def clear_vector_store() -> bool:
|
|
47
|
+
try:
|
|
48
|
+
from smartmemory.stores.vector.vector_store import VectorStore
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logging.warning("VectorStore not available: %s", e)
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
logging.info("Clearing VectorStore (if backend supports clear())...")
|
|
54
|
+
try:
|
|
55
|
+
vs = VectorStore()
|
|
56
|
+
ok = vs.clear()
|
|
57
|
+
logging.info("VectorStore cleared via clear(): %s", ok)
|
|
58
|
+
return bool(ok)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logging.error("Failed to clear VectorStore: %s", e)
|
|
61
|
+
raise
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def clear_cache() -> bool:
|
|
65
|
+
try:
|
|
66
|
+
from smartmemory.utils.cache import get_cache
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logging.warning("Cache utils not available: %s", e)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
logging.info("Clearing Redis-backed caches (best effort)...")
|
|
72
|
+
try:
|
|
73
|
+
cache = get_cache()
|
|
74
|
+
# Try to get redis client from cache object
|
|
75
|
+
client = getattr(cache, 'redis', None)
|
|
76
|
+
if client and hasattr(client, "flushdb"):
|
|
77
|
+
client.flushdb()
|
|
78
|
+
logging.info("Redis FLUSHDB successful")
|
|
79
|
+
return True
|
|
80
|
+
# Extract connection params if available
|
|
81
|
+
url = os.getenv("REDIS_URL") or os.getenv("REDIS_URI")
|
|
82
|
+
if url:
|
|
83
|
+
try:
|
|
84
|
+
import redis # type: ignore
|
|
85
|
+
r = redis.from_url(url)
|
|
86
|
+
r.flushdb()
|
|
87
|
+
logging.info("Redis FLUSHDB via REDIS_URL successful")
|
|
88
|
+
return True
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logging.warning("Redis module/connection failed: %s", e)
|
|
91
|
+
logging.warning("Could not locate a Redis client to flush; skipped.")
|
|
92
|
+
return False
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logging.error("Failed to clear caches: %s", e)
|
|
95
|
+
raise
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def main():
|
|
99
|
+
parser = argparse.ArgumentParser(description="Clear SmartMemory state across caches and stores.")
|
|
100
|
+
parser.add_argument("--all", action="store_true", help="Clear graph, vector store, and caches")
|
|
101
|
+
parser.add_argument("--graph", action="store_true", help="Clear SmartGraph")
|
|
102
|
+
parser.add_argument("--vector", action="store_true", help="Clear VectorStore")
|
|
103
|
+
parser.add_argument("--cache", action="store_true", help="Clear Redis-backed caches")
|
|
104
|
+
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose logging")
|
|
105
|
+
args = parser.parse_args()
|
|
106
|
+
|
|
107
|
+
_setup_logging(args.verbose)
|
|
108
|
+
|
|
109
|
+
if not any([args.all, args.graph, args.vector, args.cache]):
|
|
110
|
+
logging.info("No flags provided; defaulting to --all")
|
|
111
|
+
args.all = True
|
|
112
|
+
|
|
113
|
+
results: dict[str, Any] = {}
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
if args.all or args.graph:
|
|
117
|
+
results["graph"] = clear_graph()
|
|
118
|
+
if args.all or args.vector:
|
|
119
|
+
results["vector"] = clear_vector_store()
|
|
120
|
+
if args.all or args.cache:
|
|
121
|
+
results["cache"] = clear_cache()
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logging.error("One or more clear operations failed: %s", e)
|
|
124
|
+
raise
|
|
125
|
+
|
|
126
|
+
logging.info("Done. Summary: %s", results)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
main()
|