pretensor 0.1.0a0__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.
Files changed (138) hide show
  1. pretensor/__init__.py +50 -0
  2. pretensor/cli/__init__.py +1 -0
  3. pretensor/cli/commands/connections/__init__.py +7 -0
  4. pretensor/cli/commands/connections/add_remove.py +122 -0
  5. pretensor/cli/commands/connections/register.py +12 -0
  6. pretensor/cli/commands/export.py +120 -0
  7. pretensor/cli/commands/index.py +551 -0
  8. pretensor/cli/commands/list.py +65 -0
  9. pretensor/cli/commands/quickstart.py +171 -0
  10. pretensor/cli/commands/reindex.py +581 -0
  11. pretensor/cli/commands/semantic.py +190 -0
  12. pretensor/cli/commands/serve.py +129 -0
  13. pretensor/cli/commands/sync_grants.py +149 -0
  14. pretensor/cli/commands/validate.py +156 -0
  15. pretensor/cli/config_file.py +412 -0
  16. pretensor/cli/constants.py +10 -0
  17. pretensor/cli/dbt_enrichment.py +96 -0
  18. pretensor/cli/main.py +81 -0
  19. pretensor/cli/paths.py +43 -0
  20. pretensor/cli/plugin.py +52 -0
  21. pretensor/config.py +145 -0
  22. pretensor/connectors/SKILL.md +16 -0
  23. pretensor/connectors/__init__.py +29 -0
  24. pretensor/connectors/base.py +165 -0
  25. pretensor/connectors/bigquery.py +463 -0
  26. pretensor/connectors/inspect.py +321 -0
  27. pretensor/connectors/lineage_sqlglot.py +91 -0
  28. pretensor/connectors/models.py +130 -0
  29. pretensor/connectors/pg_array_parse.py +53 -0
  30. pretensor/connectors/postgres.py +921 -0
  31. pretensor/connectors/registry.py +51 -0
  32. pretensor/connectors/snapshot.py +244 -0
  33. pretensor/connectors/snowflake.py +840 -0
  34. pretensor/core/__init__.py +1 -0
  35. pretensor/core/builder.py +268 -0
  36. pretensor/core/dsn_crypto.py +40 -0
  37. pretensor/core/ids.py +68 -0
  38. pretensor/core/portable_export.py +253 -0
  39. pretensor/core/registry.py +204 -0
  40. pretensor/core/schema.py +368 -0
  41. pretensor/core/store.py +1343 -0
  42. pretensor/enrichment/__init__.py +1 -0
  43. pretensor/enrichment/dbt/__init__.py +30 -0
  44. pretensor/enrichment/dbt/lineage.py +100 -0
  45. pretensor/enrichment/dbt/manifest.py +298 -0
  46. pretensor/enrichment/dbt/metadata.py +263 -0
  47. pretensor/enrichment/dbt/pipeline.py +77 -0
  48. pretensor/enrichment/dbt/resolution.py +101 -0
  49. pretensor/enrichment/dbt/signals.py +305 -0
  50. pretensor/entities/__init__.py +27 -0
  51. pretensor/entities/builder.py +63 -0
  52. pretensor/entities/classifier.py +359 -0
  53. pretensor/entities/llm_extract.py +66 -0
  54. pretensor/graph_models/__init__.py +14 -0
  55. pretensor/graph_models/base.py +11 -0
  56. pretensor/graph_models/edge.py +35 -0
  57. pretensor/graph_models/entity.py +21 -0
  58. pretensor/graph_models/node.py +75 -0
  59. pretensor/graph_models/relationship.py +27 -0
  60. pretensor/intelligence/__init__.py +33 -0
  61. pretensor/intelligence/cluster_labeler.py +335 -0
  62. pretensor/intelligence/clustering.py +162 -0
  63. pretensor/intelligence/combining.py +32 -0
  64. pretensor/intelligence/discovery.py +114 -0
  65. pretensor/intelligence/embeddings.py +198 -0
  66. pretensor/intelligence/graph_export.py +139 -0
  67. pretensor/intelligence/heuristic.py +540 -0
  68. pretensor/intelligence/join_paths/__init__.py +130 -0
  69. pretensor/intelligence/join_paths/on_demand.py +516 -0
  70. pretensor/intelligence/join_paths/storage.py +70 -0
  71. pretensor/intelligence/llm_infer.py +78 -0
  72. pretensor/intelligence/llm_runtime.py +60 -0
  73. pretensor/intelligence/metric_templates.py +193 -0
  74. pretensor/intelligence/pipeline.py +254 -0
  75. pretensor/intelligence/schema_classification.py +306 -0
  76. pretensor/intelligence/scoring.py +66 -0
  77. pretensor/intelligence/shadow_alias.py +101 -0
  78. pretensor/intelligence/statistical.py +50 -0
  79. pretensor/intelligence/steps.py +161 -0
  80. pretensor/introspection/__init__.py +6 -0
  81. pretensor/introspection/inspector.py +5 -0
  82. pretensor/introspection/models/__init__.py +0 -0
  83. pretensor/introspection/models/base.py +5 -0
  84. pretensor/introspection/models/config.py +236 -0
  85. pretensor/introspection/models/dsn.py +442 -0
  86. pretensor/introspection/models/plan.py +116 -0
  87. pretensor/introspection/models/schema.py +10 -0
  88. pretensor/introspection/models/semantic.py +121 -0
  89. pretensor/introspection/models/validation.py +116 -0
  90. pretensor/introspection/snapshot.py +46 -0
  91. pretensor/mcp/__init__.py +16 -0
  92. pretensor/mcp/config_json.py +24 -0
  93. pretensor/mcp/payload_types.py +248 -0
  94. pretensor/mcp/resources/__init__.py +19 -0
  95. pretensor/mcp/resources/markdown.py +420 -0
  96. pretensor/mcp/server.py +723 -0
  97. pretensor/mcp/service.py +47 -0
  98. pretensor/mcp/service_context.py +126 -0
  99. pretensor/mcp/service_registry.py +242 -0
  100. pretensor/mcp/tool_registry.py +126 -0
  101. pretensor/mcp/tools/__init__.py +1 -0
  102. pretensor/mcp/tools/compile_metric.py +87 -0
  103. pretensor/mcp/tools/context.py +894 -0
  104. pretensor/mcp/tools/cypher.py +333 -0
  105. pretensor/mcp/tools/detect_changes.py +226 -0
  106. pretensor/mcp/tools/impact.py +165 -0
  107. pretensor/mcp/tools/list.py +110 -0
  108. pretensor/mcp/tools/schema.py +128 -0
  109. pretensor/mcp/tools/search.py +108 -0
  110. pretensor/mcp/tools/traverse.py +753 -0
  111. pretensor/mcp/tools/validate_sql.py +74 -0
  112. pretensor/observability.py +203 -0
  113. pretensor/search/__init__.py +6 -0
  114. pretensor/search/base.py +80 -0
  115. pretensor/search/index.py +435 -0
  116. pretensor/semantic/__init__.py +24 -0
  117. pretensor/semantic/base.py +123 -0
  118. pretensor/semantic/compiler.py +346 -0
  119. pretensor/semantic/yaml_layer.py +182 -0
  120. pretensor/skills/__init__.py +5 -0
  121. pretensor/skills/generator.py +235 -0
  122. pretensor/staleness/__init__.py +15 -0
  123. pretensor/staleness/graph_patcher.py +355 -0
  124. pretensor/staleness/impact_analyzer.py +162 -0
  125. pretensor/staleness/snapshot_store.py +38 -0
  126. pretensor/validation/__init__.py +9 -0
  127. pretensor/validation/query_validator.py +436 -0
  128. pretensor/visibility/__init__.py +23 -0
  129. pretensor/visibility/config.py +126 -0
  130. pretensor/visibility/filter.py +143 -0
  131. pretensor/visibility/kuzu_helpers.py +32 -0
  132. pretensor/visibility/runtime.py +36 -0
  133. pretensor/visibility/sync_grants.py +188 -0
  134. pretensor-0.1.0a0.dist-info/METADATA +175 -0
  135. pretensor-0.1.0a0.dist-info/RECORD +138 -0
  136. pretensor-0.1.0a0.dist-info/WHEEL +4 -0
  137. pretensor-0.1.0a0.dist-info/entry_points.txt +2 -0
  138. pretensor-0.1.0a0.dist-info/licenses/LICENSE +21 -0
pretensor/__init__.py ADDED
@@ -0,0 +1,50 @@
1
+ """Pretensor Graph — schema knowledge graph backed by Kuzu.
2
+
3
+ Heavy imports (e.g. :class:`GraphBuilder`) are lazy so ``import pretensor``
4
+ works in tests that only touch the registry.
5
+ """
6
+
7
+ # pyright: reportUnsupportedDunderAll=false
8
+ # Names in __all__ are provided via __getattr__; Pyright does not model that.
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+ __all__ = [
15
+ "GraphBuilder",
16
+ "GraphEdge",
17
+ "GraphNode",
18
+ "GraphRegistry",
19
+ "KuzuStore",
20
+ "RelationshipCandidate",
21
+ ]
22
+
23
+
24
+ def __getattr__(name: str) -> Any:
25
+ if name == "GraphBuilder":
26
+ from pretensor.core.builder import GraphBuilder
27
+
28
+ return GraphBuilder
29
+ if name == "GraphRegistry":
30
+ from pretensor.core.registry import GraphRegistry
31
+
32
+ return GraphRegistry
33
+ if name == "KuzuStore":
34
+ from pretensor.core.store import KuzuStore
35
+
36
+ return KuzuStore
37
+ if name == "GraphNode":
38
+ from pretensor.graph_models.node import GraphNode
39
+
40
+ return GraphNode
41
+ if name == "GraphEdge":
42
+ from pretensor.graph_models.edge import GraphEdge
43
+
44
+ return GraphEdge
45
+ if name == "RelationshipCandidate":
46
+ from pretensor.graph_models.relationship import RelationshipCandidate
47
+
48
+ return RelationshipCandidate
49
+ msg = f"module {__name__!r} has no attribute {name!r}"
50
+ raise AttributeError(msg)
@@ -0,0 +1 @@
1
+ """Command-line interface for Pretensor Graph."""
@@ -0,0 +1,7 @@
1
+ """Database connection registration CLI commands (``add``, ``remove``)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pretensor.cli.commands.connections.register import register_connection_commands
6
+
7
+ __all__ = ["register_connection_commands"]
@@ -0,0 +1,122 @@
1
+ """``add`` and ``remove`` cross-DB registry commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from pretensor.cli import constants as cli_constants
11
+ from pretensor.cli.config_file import (
12
+ get_cli_config,
13
+ resolve_optional_str_option,
14
+ resolve_path_option,
15
+ )
16
+ from pretensor.cli.paths import default_connection_name, unified_graph_path
17
+ from pretensor.core.dsn_crypto import DSNEncryptor
18
+ from pretensor.core.registry import GraphRegistry
19
+ from pretensor.introspection.models.dsn import (
20
+ connection_config_from_url,
21
+ registry_dialect_for,
22
+ )
23
+
24
+
25
+ def _encryptor_for(state_dir: Path) -> DSNEncryptor:
26
+ return DSNEncryptor(state_dir / "keystore")
27
+
28
+
29
+ def register_add_remove_commands(app: typer.Typer) -> None:
30
+ @app.command("add")
31
+ def add_command(
32
+ dsn: str = typer.Argument(
33
+ ..., help="Database DSN URL (postgresql:// or snowflake://)."
34
+ ),
35
+ name: str | None = typer.Option(
36
+ None,
37
+ "--name",
38
+ "-n",
39
+ help="Logical connection name (default: DB name from DSN).",
40
+ ),
41
+ state_dir: Path = typer.Option(
42
+ cli_constants.DEFAULT_STATE_DIR,
43
+ "--state-dir",
44
+ help="State directory for registry and graphs.",
45
+ file_okay=False,
46
+ dir_okay=True,
47
+ writable=True,
48
+ resolve_path=True,
49
+ ),
50
+ ctx: typer.Context = typer.Option(None, hidden=True),
51
+ ) -> None:
52
+ """Register a DSN (encrypted); run `pretensor index` to build the graph."""
53
+ cli_config = get_cli_config(ctx)
54
+ state_dir = resolve_path_option(
55
+ ctx,
56
+ param_name="state_dir",
57
+ cli_value=state_dir,
58
+ config_value=cli_config.state_dir,
59
+ )
60
+ name = resolve_optional_str_option(
61
+ ctx,
62
+ param_name="name",
63
+ cli_value=name,
64
+ config_value=cli_config.connection_defaults.name,
65
+ )
66
+ console = Console()
67
+ connection_name = name or default_connection_name(dsn)
68
+ enc = _encryptor_for(state_dir)
69
+ reg_path = state_dir / cli_constants.REGISTRY_FILENAME
70
+ reg = GraphRegistry(reg_path).load()
71
+ graph_path = unified_graph_path(state_dir)
72
+ cfg = connection_config_from_url(dsn, connection_name)
73
+ reg.upsert(
74
+ connection_name=connection_name,
75
+ database=cfg.database or connection_name,
76
+ dsn=dsn,
77
+ graph_path=graph_path,
78
+ unified_graph_path=graph_path,
79
+ encrypt_dsn=True,
80
+ encryptor=enc,
81
+ indexed_at=None,
82
+ dialect=registry_dialect_for(cfg.type),
83
+ )
84
+ reg.save()
85
+ console.print(
86
+ f"[green]Registered[/green] connection {connection_name!r} (DSN encrypted)."
87
+ )
88
+ console.print(
89
+ f"Run: pretensor index {dsn!r} --name {connection_name} --unified"
90
+ )
91
+
92
+ @app.command("remove")
93
+ def remove_command(
94
+ name: str = typer.Argument(
95
+ ..., help="Connection name to remove from registry."
96
+ ),
97
+ state_dir: Path = typer.Option(
98
+ cli_constants.DEFAULT_STATE_DIR,
99
+ "--state-dir",
100
+ file_okay=False,
101
+ dir_okay=True,
102
+ resolve_path=True,
103
+ ),
104
+ ctx: typer.Context = typer.Option(None, hidden=True),
105
+ ) -> None:
106
+ """Remove a connection from the registry (does not delete the unified graph file)."""
107
+ cli_config = get_cli_config(ctx)
108
+ state_dir = resolve_path_option(
109
+ ctx,
110
+ param_name="state_dir",
111
+ cli_value=state_dir,
112
+ config_value=cli_config.state_dir,
113
+ )
114
+ console = Console()
115
+ reg_path = state_dir / cli_constants.REGISTRY_FILENAME
116
+ reg = GraphRegistry(reg_path).load()
117
+ if reg.get(name) is None:
118
+ console.print(f"[red]Unknown connection[/red] {name!r}")
119
+ raise typer.Exit(1)
120
+ reg.remove(name)
121
+ reg.save()
122
+ console.print(f"[green]Removed[/green] {name!r} from registry.")
@@ -0,0 +1,12 @@
1
+ """Wire connection-management Typer commands onto the root app."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from pretensor.cli.commands.connections.add_remove import register_add_remove_commands
8
+
9
+
10
+ def register_connection_commands(app: typer.Typer) -> None:
11
+ """Attach ``add`` and ``remove`` to ``app``."""
12
+ register_add_remove_commands(app)
@@ -0,0 +1,120 @@
1
+ """``pretensor export`` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from pretensor.cli import constants as cli_constants
12
+ from pretensor.cli.config_file import get_cli_config, resolve_path_option
13
+ from pretensor.core.portable_export import export_graph_payload
14
+ from pretensor.core.registry import DatabaseNotFoundError, GraphRegistry
15
+ from pretensor.core.store import KuzuStore
16
+
17
+
18
+ def register_export_command(app: typer.Typer, *, console: Console) -> None:
19
+ @app.command("export")
20
+ def export_command(
21
+ database: str = typer.Option(
22
+ ...,
23
+ "--database",
24
+ "-d",
25
+ help="Registry connection name to export (see `pretensor list`).",
26
+ ),
27
+ output: Path = typer.Option(
28
+ ...,
29
+ "--output",
30
+ "-o",
31
+ help="Target JSON path for portable graph export.",
32
+ file_okay=True,
33
+ dir_okay=False,
34
+ writable=True,
35
+ resolve_path=True,
36
+ ),
37
+ state_dir: Path = typer.Option(
38
+ cli_constants.DEFAULT_STATE_DIR,
39
+ "--state-dir",
40
+ help="Directory containing registry.json and graph files.",
41
+ file_okay=False,
42
+ dir_okay=True,
43
+ resolve_path=True,
44
+ ),
45
+ ctx: typer.Context = typer.Option(
46
+ None,
47
+ hidden=True,
48
+ ),
49
+ ) -> None:
50
+ """Dump one indexed graph to portable pretty-printed JSON."""
51
+ cli_config = get_cli_config(ctx)
52
+ state_dir = resolve_path_option(
53
+ ctx,
54
+ param_name="state_dir",
55
+ cli_value=state_dir,
56
+ config_value=cli_config.state_dir,
57
+ )
58
+ reg_path = state_dir / cli_constants.REGISTRY_FILENAME
59
+ if not reg_path.exists():
60
+ console.print("[red]No registry found.[/red] Run `pretensor index` first.")
61
+ raise typer.Exit(1)
62
+ try:
63
+ reg = GraphRegistry(reg_path).load()
64
+ entry = reg.require(database)
65
+ except DatabaseNotFoundError:
66
+ console.print(
67
+ f"[red]Unknown connection {database!r}.[/red] "
68
+ "Use `pretensor list` to discover available names."
69
+ )
70
+ raise typer.Exit(1)
71
+ except Exception as exc:
72
+ console.print(
73
+ f"[red]Cannot read registry file {reg_path}:[/red] {exc}\n"
74
+ "The registry.json may be corrupt. Delete it and re-run `pretensor index`."
75
+ )
76
+ raise typer.Exit(1) from exc
77
+ graph_path = Path(entry.unified_graph_path or entry.graph_path)
78
+ if not graph_path.exists():
79
+ console.print(
80
+ f"[red]Graph file missing:[/red] {graph_path}\n"
81
+ "Re-run `pretensor index` before exporting."
82
+ )
83
+ raise typer.Exit(1)
84
+ try:
85
+ store = KuzuStore(graph_path)
86
+ except Exception as exc:
87
+ console.print(
88
+ f"[red]Cannot open graph file {graph_path}:[/red] {exc}\n"
89
+ "The file may be corrupt. Re-run `pretensor index`."
90
+ )
91
+ raise typer.Exit(1) from exc
92
+ try:
93
+ payload = export_graph_payload(
94
+ store,
95
+ connection_name=entry.connection_name,
96
+ database_name=entry.database,
97
+ graph_path=graph_path,
98
+ )
99
+ finally:
100
+ store.close()
101
+ try:
102
+ output.parent.mkdir(parents=True, exist_ok=True)
103
+ output.write_text(
104
+ json.dumps(payload, indent=2, ensure_ascii=False) + "\n",
105
+ encoding="utf-8",
106
+ )
107
+ except PermissionError as exc:
108
+ console.print(f"[red]Cannot write export file {output}:[/red] {exc}")
109
+ raise typer.Exit(1) from exc
110
+ except OSError as exc:
111
+ console.print(f"[red]Cannot write export file {output}:[/red] {exc}")
112
+ raise typer.Exit(1) from exc
113
+ stats = payload["stats"]
114
+ console.print(f"[green]Graph exported:[/green] {output}")
115
+ console.print(
116
+ "[dim]"
117
+ f"node_types={stats['node_types']} edge_types={stats['edge_types']} "
118
+ f"nodes={stats['node_count']} edges={stats['edge_count']}"
119
+ "[/dim]"
120
+ )