clm-kernel 0.0.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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Graph Extractor
|
|
3
|
+
|
|
4
|
+
Extracts entities and relationships from MCard content using LLM.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
# Data Classes
|
|
18
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Entity:
|
|
23
|
+
"""Represents an entity extracted from text."""
|
|
24
|
+
|
|
25
|
+
name: str
|
|
26
|
+
type: str # CONCEPT, TECHNOLOGY, PERSON, ORGANIZATION, etc.
|
|
27
|
+
description: str = ""
|
|
28
|
+
id: int | None = None
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> dict[str, Any]:
|
|
31
|
+
return {
|
|
32
|
+
"name": self.name,
|
|
33
|
+
"type": self.type,
|
|
34
|
+
"description": self.description,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Relationship:
|
|
40
|
+
"""Represents a relationship between two entities."""
|
|
41
|
+
|
|
42
|
+
source: str # Entity name
|
|
43
|
+
target: str # Entity name
|
|
44
|
+
relationship: str # Verb/action connecting them
|
|
45
|
+
description: str = ""
|
|
46
|
+
weight: float = 1.0
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> dict[str, Any]:
|
|
49
|
+
return {
|
|
50
|
+
"source": self.source,
|
|
51
|
+
"target": self.target,
|
|
52
|
+
"relationship": self.relationship,
|
|
53
|
+
"description": self.description,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ExtractionResult:
|
|
59
|
+
"""Result from entity/relationship extraction."""
|
|
60
|
+
|
|
61
|
+
entities: list[Entity] = field(default_factory=list)
|
|
62
|
+
relationships: list[Relationship] = field(default_factory=list)
|
|
63
|
+
success: bool = True
|
|
64
|
+
error: str | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
# Extraction Prompts
|
|
69
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
EXTRACTION_SYSTEM_PROMPT = """You are an expert at extracting structured information from text.
|
|
72
|
+
Given a text, identify:
|
|
73
|
+
1. ENTITIES: Named concepts, technologies, people, organizations, or things
|
|
74
|
+
2. RELATIONSHIPS: How entities relate to each other
|
|
75
|
+
|
|
76
|
+
Respond ONLY with valid JSON in this format:
|
|
77
|
+
{
|
|
78
|
+
"entities": [
|
|
79
|
+
{"name": "EntityName", "type": "CONCEPT|TECHNOLOGY|PERSON|ORGANIZATION|OTHER", "description": "Brief description"}
|
|
80
|
+
],
|
|
81
|
+
"relationships": [
|
|
82
|
+
{"source": "Entity1", "target": "Entity2", "relationship": "verb phrase", "description": "Optional context"}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Entity types:
|
|
87
|
+
- CONCEPT: Abstract ideas, methodologies, patterns (e.g., "content-addressable storage")
|
|
88
|
+
- TECHNOLOGY: Systems, libraries, frameworks (e.g., "SQLite", "Python")
|
|
89
|
+
- PERSON: People names
|
|
90
|
+
- ORGANIZATION: Companies, groups
|
|
91
|
+
- OTHER: Anything else
|
|
92
|
+
|
|
93
|
+
Keep entity names concise but unique. Use present tense for relationships."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
EXTRACTION_USER_PROMPT = """Extract entities and relationships from this text:
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
{content}
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
Remember: Return ONLY valid JSON."""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
106
|
+
# Graph Extractor
|
|
107
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class GraphExtractor:
|
|
111
|
+
"""
|
|
112
|
+
Extracts entities and relationships from text using LLM.
|
|
113
|
+
|
|
114
|
+
Usage:
|
|
115
|
+
extractor = GraphExtractor(model='qwen3:0.6b')
|
|
116
|
+
result = extractor.extract("MCard is a Python library...")
|
|
117
|
+
|
|
118
|
+
for entity in result.entities:
|
|
119
|
+
print(f"{entity.name} ({entity.type})")
|
|
120
|
+
|
|
121
|
+
for rel in result.relationships:
|
|
122
|
+
print(f"{rel.source} --{rel.relationship}-> {rel.target}")
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
model: str = "qwen3:0.6b",
|
|
128
|
+
temperature: float = 0.1,
|
|
129
|
+
max_retries: int = 2,
|
|
130
|
+
):
|
|
131
|
+
"""
|
|
132
|
+
Initialize graph extractor.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
model: LLM model to use
|
|
136
|
+
temperature: Low for consistent extraction
|
|
137
|
+
max_retries: Retries on parse failure
|
|
138
|
+
"""
|
|
139
|
+
self.model = model
|
|
140
|
+
self.temperature = temperature
|
|
141
|
+
self.max_retries = max_retries
|
|
142
|
+
|
|
143
|
+
def extract(self, content: str) -> ExtractionResult:
|
|
144
|
+
"""
|
|
145
|
+
Extract entities and relationships from content.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
content: Text to extract from
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
ExtractionResult with entities and relationships
|
|
152
|
+
"""
|
|
153
|
+
if not content or not content.strip():
|
|
154
|
+
return ExtractionResult(success=False, error="Empty content")
|
|
155
|
+
|
|
156
|
+
# Truncate if too long
|
|
157
|
+
max_chars = 6000
|
|
158
|
+
if len(content) > max_chars:
|
|
159
|
+
content = content[:max_chars] + "\n[...truncated...]"
|
|
160
|
+
|
|
161
|
+
# Call LLM for extraction
|
|
162
|
+
for attempt in range(self.max_retries + 1):
|
|
163
|
+
try:
|
|
164
|
+
result = self._call_llm(content)
|
|
165
|
+
parsed = self._parse_response(result)
|
|
166
|
+
if parsed.entities or parsed.relationships:
|
|
167
|
+
return parsed
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.warning(f"Extraction attempt {attempt + 1} failed: {e}")
|
|
170
|
+
if attempt == self.max_retries:
|
|
171
|
+
return ExtractionResult(success=False, error=str(e))
|
|
172
|
+
|
|
173
|
+
return ExtractionResult(success=False, error="No entities extracted")
|
|
174
|
+
|
|
175
|
+
def _call_llm(self, content: str) -> str:
|
|
176
|
+
"""Call LLM for extraction."""
|
|
177
|
+
from clm.layer3.agency import chat_monad
|
|
178
|
+
|
|
179
|
+
prompt = EXTRACTION_USER_PROMPT.format(content=content)
|
|
180
|
+
|
|
181
|
+
result = chat_monad(
|
|
182
|
+
prompt=prompt,
|
|
183
|
+
system_prompt=EXTRACTION_SYSTEM_PROMPT,
|
|
184
|
+
model=self.model,
|
|
185
|
+
temperature=self.temperature,
|
|
186
|
+
max_tokens=2000,
|
|
187
|
+
).unsafe_run()
|
|
188
|
+
|
|
189
|
+
if result.is_left():
|
|
190
|
+
raise RuntimeError(f"LLM call failed: {result.value}")
|
|
191
|
+
|
|
192
|
+
return result.value.get("content", str(result.value))
|
|
193
|
+
|
|
194
|
+
def _parse_response(self, response: str) -> ExtractionResult:
|
|
195
|
+
"""Parse LLM response into structured data."""
|
|
196
|
+
# Try to extract JSON from response
|
|
197
|
+
json_match = re.search(r"\{[\s\S]*\}", response)
|
|
198
|
+
if not json_match:
|
|
199
|
+
raise ValueError("No JSON found in response")
|
|
200
|
+
|
|
201
|
+
json_str = json_match.group()
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
data = json.loads(json_str)
|
|
205
|
+
except json.JSONDecodeError:
|
|
206
|
+
# Try to fix common issues
|
|
207
|
+
json_str = self._clean_json(json_str)
|
|
208
|
+
data = json.loads(json_str)
|
|
209
|
+
|
|
210
|
+
entities = []
|
|
211
|
+
for e in data.get("entities", []):
|
|
212
|
+
if isinstance(e, dict) and "name" in e:
|
|
213
|
+
entities.append(
|
|
214
|
+
Entity(
|
|
215
|
+
name=e.get("name", "").strip(),
|
|
216
|
+
type=e.get("type", "OTHER").upper(),
|
|
217
|
+
description=e.get("description", "").strip(),
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
relationships = []
|
|
222
|
+
for r in data.get("relationships", []):
|
|
223
|
+
if isinstance(r, dict) and "source" in r and "target" in r:
|
|
224
|
+
relationships.append(
|
|
225
|
+
Relationship(
|
|
226
|
+
source=r.get("source", "").strip(),
|
|
227
|
+
target=r.get("target", "").strip(),
|
|
228
|
+
relationship=r.get("relationship", "relates_to").strip(),
|
|
229
|
+
description=r.get("description", "").strip(),
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return ExtractionResult(
|
|
234
|
+
entities=entities, relationships=relationships, success=True
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def _clean_json(self, json_str: str) -> str:
|
|
238
|
+
"""Try to clean up malformed JSON."""
|
|
239
|
+
# Remove trailing commas
|
|
240
|
+
json_str = re.sub(r",\s*([}\]])", r"\1", json_str)
|
|
241
|
+
# Remove control characters
|
|
242
|
+
json_str = re.sub(r"[\x00-\x1f]", "", json_str)
|
|
243
|
+
return json_str
|
|
244
|
+
|
|
245
|
+
def extract_batch(self, contents: list[str]) -> list[ExtractionResult]:
|
|
246
|
+
"""
|
|
247
|
+
Extract from multiple texts.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
contents: List of texts
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
List of ExtractionResults
|
|
254
|
+
"""
|
|
255
|
+
return [self.extract(content) for content in contents]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Graph Database Schema
|
|
3
|
+
|
|
4
|
+
This module provides graph-specific schemas from the unified schema singleton.
|
|
5
|
+
|
|
6
|
+
IMPORTANT: All schemas are loaded from schema/mcard_schema.sql
|
|
7
|
+
This module provides convenient access to graph-related schemas only.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from clm.layer3.rag.graph.schema import GRAPH_SCHEMAS
|
|
11
|
+
|
|
12
|
+
Or better, use the singleton directly:
|
|
13
|
+
from clm.schemas import MCardSchema
|
|
14
|
+
schema = MCardSchema.get_instance()
|
|
15
|
+
schema.init_graph_tables(conn)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import typing
|
|
19
|
+
|
|
20
|
+
from clm.schemas import MCardSchema
|
|
21
|
+
|
|
22
|
+
if typing.TYPE_CHECKING:
|
|
23
|
+
GRAPH_SCHEMAS: dict
|
|
24
|
+
ENTITY_SCHEMA: str
|
|
25
|
+
ENTITY_INDEX_NAME: str
|
|
26
|
+
ENTITY_INDEX_TYPE: str
|
|
27
|
+
ENTITY_INDEX_SOURCE: str
|
|
28
|
+
RELATIONSHIP_SCHEMA: str
|
|
29
|
+
RELATIONSHIP_INDEX_SOURCE: str
|
|
30
|
+
RELATIONSHIP_INDEX_TARGET: str
|
|
31
|
+
COMMUNITY_SCHEMA: str
|
|
32
|
+
COMMUNITY_INDEX_LEVEL: str
|
|
33
|
+
EXTRACTION_TRACKING_SCHEMA: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_schema_instance() -> MCardSchema:
|
|
37
|
+
return MCardSchema.get_instance()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _build_graph_schemas() -> dict:
|
|
41
|
+
"""Build GRAPH_SCHEMAS from the unified schema."""
|
|
42
|
+
schema = _get_schema_instance()
|
|
43
|
+
return {
|
|
44
|
+
"entities": schema.get_table("graph_entities"),
|
|
45
|
+
"entity_idx_name": schema.get_index("idx_entity_name"),
|
|
46
|
+
"entity_idx_type": schema.get_index("idx_entity_type"),
|
|
47
|
+
"entity_idx_source": schema.get_index("idx_entity_source"),
|
|
48
|
+
"relationships": schema.get_table("graph_relationships"),
|
|
49
|
+
"rel_idx_source": schema.get_index("idx_rel_source"),
|
|
50
|
+
"rel_idx_target": schema.get_index("idx_rel_target"),
|
|
51
|
+
"communities": schema.get_table("graph_communities"),
|
|
52
|
+
"community_idx_level": schema.get_index("idx_community_level"),
|
|
53
|
+
"extractions": schema.get_table("graph_extractions"),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Lazy-loaded schema dictionary
|
|
58
|
+
_GRAPH_SCHEMAS = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def __getattr__(name: str):
|
|
62
|
+
"""Lazy loading of schema dictionaries."""
|
|
63
|
+
global _GRAPH_SCHEMAS
|
|
64
|
+
|
|
65
|
+
if name == "GRAPH_SCHEMAS":
|
|
66
|
+
if _GRAPH_SCHEMAS is None:
|
|
67
|
+
_GRAPH_SCHEMAS = _build_graph_schemas()
|
|
68
|
+
return _GRAPH_SCHEMAS
|
|
69
|
+
|
|
70
|
+
elif name == "ENTITY_SCHEMA":
|
|
71
|
+
return _get_schema_instance().get_table("graph_entities")
|
|
72
|
+
|
|
73
|
+
elif name == "ENTITY_INDEX_NAME":
|
|
74
|
+
return _get_schema_instance().get_index("idx_entity_name")
|
|
75
|
+
|
|
76
|
+
elif name == "ENTITY_INDEX_TYPE":
|
|
77
|
+
return _get_schema_instance().get_index("idx_entity_type")
|
|
78
|
+
|
|
79
|
+
elif name == "ENTITY_INDEX_SOURCE":
|
|
80
|
+
return _get_schema_instance().get_index("idx_entity_source")
|
|
81
|
+
|
|
82
|
+
elif name == "RELATIONSHIP_SCHEMA":
|
|
83
|
+
return _get_schema_instance().get_table("graph_relationships")
|
|
84
|
+
|
|
85
|
+
elif name == "RELATIONSHIP_INDEX_SOURCE":
|
|
86
|
+
return _get_schema_instance().get_index("idx_rel_source")
|
|
87
|
+
|
|
88
|
+
elif name == "RELATIONSHIP_INDEX_TARGET":
|
|
89
|
+
return _get_schema_instance().get_index("idx_rel_target")
|
|
90
|
+
|
|
91
|
+
elif name == "COMMUNITY_SCHEMA":
|
|
92
|
+
return _get_schema_instance().get_table("graph_communities")
|
|
93
|
+
|
|
94
|
+
elif name == "COMMUNITY_INDEX_LEVEL":
|
|
95
|
+
return _get_schema_instance().get_index("idx_community_level")
|
|
96
|
+
|
|
97
|
+
elif name == "EXTRACTION_TRACKING_SCHEMA":
|
|
98
|
+
return _get_schema_instance().get_table("graph_extractions")
|
|
99
|
+
|
|
100
|
+
raise AttributeError(f"module 'mcard.rag.graph.schema' has no attribute '{name}'")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
"GRAPH_SCHEMAS",
|
|
105
|
+
"ENTITY_SCHEMA",
|
|
106
|
+
"ENTITY_INDEX_NAME",
|
|
107
|
+
"ENTITY_INDEX_TYPE",
|
|
108
|
+
"ENTITY_INDEX_SOURCE",
|
|
109
|
+
"RELATIONSHIP_SCHEMA",
|
|
110
|
+
"RELATIONSHIP_INDEX_SOURCE",
|
|
111
|
+
"RELATIONSHIP_INDEX_TARGET",
|
|
112
|
+
"COMMUNITY_SCHEMA",
|
|
113
|
+
"COMMUNITY_INDEX_LEVEL",
|
|
114
|
+
"EXTRACTION_TRACKING_SCHEMA",
|
|
115
|
+
]
|