helios-cortex 0.1.0__tar.gz

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 (124) hide show
  1. helios_cortex-0.1.0/.gitignore +21 -0
  2. helios_cortex-0.1.0/CONTRACTS.md +108 -0
  3. helios_cortex-0.1.0/PKG-INFO +38 -0
  4. helios_cortex-0.1.0/pyproject.toml +79 -0
  5. helios_cortex-0.1.0/src/helios_cortex/__init__.py +18 -0
  6. helios_cortex-0.1.0/src/helios_cortex/_optional.py +17 -0
  7. helios_cortex-0.1.0/src/helios_cortex/_pending/__init__.py +8 -0
  8. helios_cortex-0.1.0/src/helios_cortex/_pending/browse.py +181 -0
  9. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler/__init__.py +6 -0
  10. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler/presets/index.yaml +13 -0
  11. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler/presets/retail-returns.yaml +535 -0
  12. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler/settings.py +566 -0
  13. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler/sources.py +144 -0
  14. helios_cortex-0.1.0/src/helios_cortex/_pending/crawler_settings.py +94 -0
  15. helios_cortex-0.1.0/src/helios_cortex/_pending/evidence.py +416 -0
  16. helios_cortex-0.1.0/src/helios_cortex/artifact_storage.py +289 -0
  17. helios_cortex-0.1.0/src/helios_cortex/artifacts.py +376 -0
  18. helios_cortex-0.1.0/src/helios_cortex/audit_sink.py +86 -0
  19. helios_cortex-0.1.0/src/helios_cortex/caller.py +46 -0
  20. helios_cortex-0.1.0/src/helios_cortex/catalog/__init__.py +21 -0
  21. helios_cortex-0.1.0/src/helios_cortex/catalog/base.py +91 -0
  22. helios_cortex-0.1.0/src/helios_cortex/compiler/__init__.py +9 -0
  23. helios_cortex-0.1.0/src/helios_cortex/compiler/compiler.py +402 -0
  24. helios_cortex-0.1.0/src/helios_cortex/conformance.py +364 -0
  25. helios_cortex-0.1.0/src/helios_cortex/data_authorization.py +222 -0
  26. helios_cortex-0.1.0/src/helios_cortex/domain.py +118 -0
  27. helios_cortex-0.1.0/src/helios_cortex/engines/__init__.py +16 -0
  28. helios_cortex-0.1.0/src/helios_cortex/engines/base.py +108 -0
  29. helios_cortex-0.1.0/src/helios_cortex/engines/dialect.py +62 -0
  30. helios_cortex-0.1.0/src/helios_cortex/engines/duckdb_engine.py +55 -0
  31. helios_cortex-0.1.0/src/helios_cortex/engines/sqlalchemy_engine.py +121 -0
  32. helios_cortex-0.1.0/src/helios_cortex/engines/trino_engine.py +132 -0
  33. helios_cortex-0.1.0/src/helios_cortex/index/__init__.py +46 -0
  34. helios_cortex-0.1.0/src/helios_cortex/index/__main__.py +35 -0
  35. helios_cortex-0.1.0/src/helios_cortex/index/ids.py +63 -0
  36. helios_cortex-0.1.0/src/helios_cortex/index/mappings.py +128 -0
  37. helios_cortex-0.1.0/src/helios_cortex/index/ontology_versions.py +104 -0
  38. helios_cortex-0.1.0/src/helios_cortex/index/records.py +300 -0
  39. helios_cortex-0.1.0/src/helios_cortex/index/runs.py +102 -0
  40. helios_cortex-0.1.0/src/helios_cortex/index/schema_impala.sql +201 -0
  41. helios_cortex-0.1.0/src/helios_cortex/index/store.py +123 -0
  42. helios_cortex-0.1.0/src/helios_cortex/index/tables.py +142 -0
  43. helios_cortex-0.1.0/src/helios_cortex/llm/__init__.py +17 -0
  44. helios_cortex-0.1.0/src/helios_cortex/llm/client.py +535 -0
  45. helios_cortex-0.1.0/src/helios_cortex/ontology/__init__.py +35 -0
  46. helios_cortex-0.1.0/src/helios_cortex/ontology/graph.py +209 -0
  47. helios_cortex-0.1.0/src/helios_cortex/ontology/mapping.py +439 -0
  48. helios_cortex-0.1.0/src/helios_cortex/ontology/parser.py +336 -0
  49. helios_cortex-0.1.0/src/helios_cortex/ossie/__init__.py +2 -0
  50. helios_cortex-0.1.0/src/helios_cortex/ossie/format.py +43 -0
  51. helios_cortex-0.1.0/src/helios_cortex/ossie/model.py +263 -0
  52. helios_cortex-0.1.0/src/helios_cortex/ossie/ossie-schema.json +367 -0
  53. helios_cortex-0.1.0/src/helios_cortex/plugins.py +128 -0
  54. helios_cortex-0.1.0/src/helios_cortex/py.typed +0 -0
  55. helios_cortex-0.1.0/src/helios_cortex/registry/__init__.py +214 -0
  56. helios_cortex-0.1.0/src/helios_cortex/registry/__main__.py +27 -0
  57. helios_cortex-0.1.0/src/helios_cortex/registry/migrations/env.py +18 -0
  58. helios_cortex-0.1.0/src/helios_cortex/registry/migrations/script.py.mako +20 -0
  59. helios_cortex-0.1.0/src/helios_cortex/registry/migrations/versions/0001_registry.py +73 -0
  60. helios_cortex-0.1.0/src/helios_cortex/registry/sql.py +312 -0
  61. helios_cortex-0.1.0/src/helios_cortex/runs.py +517 -0
  62. helios_cortex-0.1.0/src/helios_cortex/settings.py +355 -0
  63. helios_cortex-0.1.0/src/helios_cortex/visibility.py +46 -0
  64. helios_cortex-0.1.0/tests/conftest.py +11 -0
  65. helios_cortex-0.1.0/tests/fixtures/sample_requests.json +28 -0
  66. helios_cortex-0.1.0/tests/fixtures/tpcds-harvest.json +649 -0
  67. helios_cortex-0.1.0/tests/golden/compile/bigquery/ad_hoc_measures.sql +14 -0
  68. helios_cortex-0.1.0/tests/golden/compile/bigquery/filtered_and_ordered.sql +19 -0
  69. helios_cortex-0.1.0/tests/golden/compile/bigquery/pinned_relationship.sql +8 -0
  70. helios_cortex-0.1.0/tests/golden/compile/bigquery/revenue_by_year.sql +8 -0
  71. helios_cortex-0.1.0/tests/golden/compile/bigquery/snowflake_join.sql +16 -0
  72. helios_cortex-0.1.0/tests/golden/compile/duckdb/ad_hoc_measures.sql +14 -0
  73. helios_cortex-0.1.0/tests/golden/compile/duckdb/filtered_and_ordered.sql +19 -0
  74. helios_cortex-0.1.0/tests/golden/compile/duckdb/pinned_relationship.sql +8 -0
  75. helios_cortex-0.1.0/tests/golden/compile/duckdb/revenue_by_year.sql +8 -0
  76. helios_cortex-0.1.0/tests/golden/compile/duckdb/snowflake_join.sql +16 -0
  77. helios_cortex-0.1.0/tests/golden/compile/postgres/ad_hoc_measures.sql +14 -0
  78. helios_cortex-0.1.0/tests/golden/compile/postgres/filtered_and_ordered.sql +19 -0
  79. helios_cortex-0.1.0/tests/golden/compile/postgres/pinned_relationship.sql +8 -0
  80. helios_cortex-0.1.0/tests/golden/compile/postgres/revenue_by_year.sql +8 -0
  81. helios_cortex-0.1.0/tests/golden/compile/postgres/snowflake_join.sql +16 -0
  82. helios_cortex-0.1.0/tests/golden/compile/snowflake/ad_hoc_measures.sql +14 -0
  83. helios_cortex-0.1.0/tests/golden/compile/snowflake/filtered_and_ordered.sql +19 -0
  84. helios_cortex-0.1.0/tests/golden/compile/snowflake/pinned_relationship.sql +8 -0
  85. helios_cortex-0.1.0/tests/golden/compile/snowflake/revenue_by_year.sql +8 -0
  86. helios_cortex-0.1.0/tests/golden/compile/snowflake/snowflake_join.sql +16 -0
  87. helios_cortex-0.1.0/tests/golden/compile/trino/ad_hoc_measures.sql +14 -0
  88. helios_cortex-0.1.0/tests/golden/compile/trino/filtered_and_ordered.sql +19 -0
  89. helios_cortex-0.1.0/tests/golden/compile/trino/pinned_relationship.sql +8 -0
  90. helios_cortex-0.1.0/tests/golden/compile/trino/revenue_by_year.sql +8 -0
  91. helios_cortex-0.1.0/tests/golden/compile/trino/snowflake_join.sql +16 -0
  92. helios_cortex-0.1.0/tests/ozone/docker-compose.yaml +24 -0
  93. helios_cortex-0.1.0/tests/ozone/docker-config +29 -0
  94. helios_cortex-0.1.0/tests/test_artifact_storage.py +116 -0
  95. helios_cortex-0.1.0/tests/test_artifacts.py +208 -0
  96. helios_cortex-0.1.0/tests/test_audit_sink.py +63 -0
  97. helios_cortex-0.1.0/tests/test_caller.py +94 -0
  98. helios_cortex-0.1.0/tests/test_catalog_plugins.py +101 -0
  99. helios_cortex-0.1.0/tests/test_compile_golden.py +45 -0
  100. helios_cortex-0.1.0/tests/test_compiler.py +79 -0
  101. helios_cortex-0.1.0/tests/test_conformance.py +72 -0
  102. helios_cortex-0.1.0/tests/test_contracts.py +42 -0
  103. helios_cortex-0.1.0/tests/test_data_policy.py +125 -0
  104. helios_cortex-0.1.0/tests/test_dialects.py +57 -0
  105. helios_cortex-0.1.0/tests/test_engine_identity.py +60 -0
  106. helios_cortex-0.1.0/tests/test_engine_plugins.py +123 -0
  107. helios_cortex-0.1.0/tests/test_environment_reads.py +70 -0
  108. helios_cortex-0.1.0/tests/test_import_side_effects.py +117 -0
  109. helios_cortex-0.1.0/tests/test_index_mappings.py +147 -0
  110. helios_cortex-0.1.0/tests/test_index_ontology_versions.py +76 -0
  111. helios_cortex-0.1.0/tests/test_index_schema.py +214 -0
  112. helios_cortex-0.1.0/tests/test_logging_rules.py +55 -0
  113. helios_cortex-0.1.0/tests/test_ontology_mapping.py +176 -0
  114. helios_cortex-0.1.0/tests/test_ontology_parser.py +203 -0
  115. helios_cortex-0.1.0/tests/test_optional_dependencies.py +133 -0
  116. helios_cortex-0.1.0/tests/test_plugins.py +98 -0
  117. helios_cortex-0.1.0/tests/test_profile_engines.py +56 -0
  118. helios_cortex-0.1.0/tests/test_registry.py +52 -0
  119. helios_cortex-0.1.0/tests/test_registry_records.py +24 -0
  120. helios_cortex-0.1.0/tests/test_registry_sql.py +120 -0
  121. helios_cortex-0.1.0/tests/test_settings.py +216 -0
  122. helios_cortex-0.1.0/tests/test_trino_engine.py +170 -0
  123. helios_cortex-0.1.0/tests/test_trino_live.py +72 -0
  124. helios_cortex-0.1.0/tests/test_visibility.py +40 -0
@@ -0,0 +1,21 @@
1
+ tpcds/
2
+ *.parquet
3
+ __pycache__/
4
+ .env
5
+ .env.sh
6
+ /requirements.lock
7
+ .helios-python/
8
+ state/
9
+ *.db-wal
10
+ *.db-shm
11
+ node_modules/
12
+ apps/ui/dist/
13
+ apps/helios/ui/dist/
14
+ apps/helios/ui/src/content/docs/
15
+ *.tsbuildinfo
16
+ .cursor/
17
+ runs/
18
+ run_archive/
19
+ .DS_Store
20
+ *.pptx
21
+ .venv/
@@ -0,0 +1,108 @@
1
+ # helios-cortex contracts
2
+
3
+ This file is helios-cortex's public API. The names in the tables below are the semantic-versioning
4
+ boundary: other packages (helios-model, helios-serve, helios-mcp, plugins and host
5
+ applications) may depend on them, and they change only as the policy below allows. Everything
6
+ else in `helios_cortex` is internal, and can change or disappear in any release.
7
+
8
+ `tests/test_contracts.py` imports every name listed here from the module given, so the tables
9
+ cannot drift from the code.
10
+
11
+ ## Stability levels
12
+
13
+ - **Stable.** Changed only under the deprecation policy.
14
+ - **Provisional.** Public, but a named burndown task will reshape it. Depend on it if you need
15
+ to, and expect to update your code when that task lands. It becomes Stable afterwards.
16
+ - **Planned.** Not written yet. Listed so that the boundary is complete.
17
+
18
+ ## Deprecation policy
19
+
20
+ helios-cortex is pre-1.0 (0.y.z).
21
+
22
+ - **While pre-1.0:** a release that removes or changes a Stable name bumps the minor version (y).
23
+ Where it can, the old spelling keeps working for one more minor release and emits a
24
+ `DeprecationWarning` naming its replacement. Patch releases (z) never change a Stable name.
25
+ - **From 1.0:** standard semantic versioning. A Stable name is deprecated in a minor release
26
+ (with a `DeprecationWarning` and a changelog entry) and removed no earlier than the next
27
+ major release.
28
+ - **What counts as a change:** removing or renaming a name, removing a parameter or field,
29
+ making an optional parameter required, narrowing an accepted type, or changing the meaning
30
+ of a return value. Adding optional parameters, fields or methods is not a change.
31
+ - **Provisional names:** may change in any minor release. The changelog says how to migrate.
32
+
33
+ ## Contract
34
+
35
+ ### Configuration
36
+
37
+ | Module | Names | Level | Notes |
38
+ |---|---|---|---|
39
+ | `helios_cortex.settings` | `Settings`, `ConnectionSettings`, `CatalogSettings`, `LLMSettings` | Stable | Resolution order, the config file and named connections and catalogs. A connection's `identity` (`service`, the default, `impersonate` or `token`) picks the engine identity mode. Core reads `HELIOS_*` and the LLM ecosystem's names (`ANTHROPIC_API_KEY`, `MISTRAL_API_KEY`, `LLM_PROVIDER`). A subclass adds a platform's variables by overriding `profile_sources` (after the conventional environment, before the config file); helios-cloudera's `WorkbenchSettings` reads the Workbench and Cloudera names. `registry_url` (secret) names the model registry's database. `artifact_storage` (`location` plus filesystem options, secrets hidden) puts artifacts on object storage. |
40
+
41
+ ### Semantic model and compiler
42
+
43
+ | Module | Names | Level | Notes |
44
+ |---|---|---|---|
45
+ | `helios_cortex.ossie.model` | `SemanticModel`, `Dataset`, `Field`, `Relationship`, `Metric` | Stable | The in-memory form of a published Ossie model. |
46
+ | `helios_cortex.ossie` | `validate`, `dump_yaml`, `SPEC_VERSION` | Provisional | The Ossie format (`helios_cortex.ossie.format`): the spec version Helios writes, validation against the vendored schema plus referential checks, and YAML output. Building a document from a discovery proposal is helios-model's (`helios_model.build`). |
47
+ | `helios_cortex.compiler` | `Compiler`, `SemanticRequest`, `Filter`, `Measure`, `Compiled`, `CompileError`, `FilterValueTypeError` | Provisional | `compile(request, dialect)` needs a dialect: the engine's `dialect` or a SQLGlot dialect name; there is no default. |
48
+
49
+ ### Ontology
50
+
51
+ | Module | Names | Level | Notes |
52
+ |---|---|---|---|
53
+ | `helios_cortex.ontology` | `OntologyGraph`, `GraphNode`, `GraphEdge`, `OntologyGraphError` | Stable | |
54
+ | `helios_cortex.ontology` | `SourceMapping`, `load_mapping`, `load_mappings` | Stable | |
55
+
56
+ ### Index
57
+
58
+ | Module | Names | Level | Notes |
59
+ |---|---|---|---|
60
+ | `helios_cortex.index` | `IndexStore` | Stable | |
61
+ | `helios_cortex.index` | `OntologyVersionRecord`, `OntologyActivationRecord`, `CrawlRunRecord`, `AssetRecord`, `SegmentRecord`, `MentionRecord`, `EntityRecord`, `EntityLinkRecord`, `RelationshipRecord`, `ClaimRecord`, `ClaimEvidenceRecord`, `EvaluationRecord` | Stable | Rows of the `helios_index` tables. New optional fields are allowed. |
62
+
63
+ ### Storage
64
+
65
+ | Module | Names | Level | Notes |
66
+ |---|---|---|---|
67
+ | `helios_cortex.artifacts` | `ArtifactStore`, `Activation`, `ActivationConflict`, `ArtifactError`, `ANY` | Provisional | On a local directory or object storage (`ArtifactStore.from_settings`: `[artifact_storage]` else `artifacts_dir`). Published revisions are immutable (named by their YAML's SHA-256, written once); the active revision is the newest entry of `models/<id>/active/`, and `activate(..., expected_sequence=)` is a compare-and-swap (`ActivationConflict`). `read_published_ossie` / `read_revision` work on every backend and verify hashes; methods returning a local `Path` work on a local directory only. Discovery runs live in the same storage under `runs/` (`ArtifactStore.runs`). |
68
+ | `helios_cortex.artifact_storage` | `ArtifactStorage`, `storage_for`, `LocalStorage` | Provisional | `storage_for(location, **options)`: a path or `file://`, `s3://` (`[s3]`), `abfs://` / `abfss://` / `az://` (`[azure]`), `gs://` (`[gcs]`), `ofs://` through Ozone HttpFS (`[ozone]`). The one conditional operation is `create` (write only if absent, atomically), on which immutability and the activation log are built. |
69
+ | `helios_cortex.runs` | `RunStore` | Provisional | Discovery runs, `<run_id>/{harvest,profile,propose,review}.json`: `write`, `load`, `run_ids`, `latest_run_id`, `list_runs`, `detail`. On any storage: `ArtifactStore.runs` (under `runs/`), `RunStore.from_settings` (with the artifacts on object storage, else `runs_dir`), or `RunStore(path)` for a local runs directory. `runs_dir` is a local path, None on object storage. |
70
+ | `helios_cortex.conformance` | `RegistryConformance`, `ArtifactStorageConformance` | Provisional | The tests every backend must pass, Helios's and third parties': subclass one and provide a `registry` or `storage` fixture returning an empty backend. The registry suite checks the `ModelRegistry` rules, including concurrent writers; the storage suite checks the operations (byte-exact content, listing at the prefix boundary, `create` atomic under 10 racing writers) and runs `ArtifactStore` and `RunStore` on the backend. Needs the `[testing]` extra (pytest). New tests may be added in minor releases; a backend that passed may then fail, which is the point. |
71
+ | `helios_cortex.registry` | `ModelRegistry`, `InMemoryRegistry`, `SqlRegistry`, `RegisteredModel`, `SemanticRevision`, `RegistryError`, `RegistryNotReady`, `RevisionConflict`, `DRAFT`, `PUBLISHED` | Provisional | What libraries need to find models: data sources, models (status, creation) and their immutable semantic revisions. Every backend keeps the rules in the module docstring and passes `RegistryConformance`. `InMemoryRegistry` is the reference; `SqlRegistry` stores the records in any SQLAlchemy database by URL (`Settings.registry_url`, default a SQLite file in the data directory), with Alembic migrations in the package (`python -m helios_cortex.registry upgrade`; version table `helios_registry_version`, tables prefixed `helios_`); connections are checked before use, so a database failover does not fail the next call). No ownership: which models a caller sees is the host's `ModelVisibility`. The old console keeps its own SQLite repository until Phase 8, sharing `SemanticRevision`. |
72
+ | `helios_cortex.domain` | `Model`, `DataSource`, `DataSourceReference` | Provisional | Registry records with no ownership (organizations left in 2.6). The registry stores them as they are. |
73
+
74
+ ### Engines and LLM
75
+
76
+ | Module | Names | Level | Notes |
77
+ |---|---|---|---|
78
+ | `helios_cortex.engines` | `Engine`, `QueryResult`, `engine_for`, `SqlDialect`, `dialect_for` | Provisional | An engine plugin (`helios.engines`) is an `Engine` subclass whose `from_connection(ConnectionSettings)` validates the connection's options and honours its `identity`; `engine_for(connection)` loads the plugin a connection names and builds it. `query(sql, caller=None, *, limit)` runs as the service identity with no caller, and as the caller under the connection's identity mode otherwise; `identity_modes` lists the modes an engine supports. Plugins register under `helios.engines` (`helios_cortex.plugins`). An engine's `dialect` (a `SqlDialect`) renders the compiler's queries and Helios's own statements; plugins subclass it for what SQLGlot lacks (`render_query`, `count_distinct`, `identifier`). The catalog and statistics methods (`databases`, `tables`, `columns`, `row_count`, `recent_queries`) default to `information_schema` and `COUNT(*)`; each takes a keyword-only `caller=None` with the same meaning as `query`'s, so a harvest sees what its caller may see, and a plugin that overrides one must accept and honour it. |
79
+ | `helios_cortex.engines.duckdb_engine` | `DuckDBEngine`, `DuckDBOptions` | Provisional | `engine = "duckdb"`, option `path` (default `:memory:`); `service` mode only. |
80
+ | `helios_cortex.engines.sqlalchemy_engine` | `SqlAlchemyEngine`, `SqlAlchemyOptions` | Provisional | `engine = "sqlalchemy"`, option `url`; needs the `[sqlalchemy]` extra and the database's driver. `service` mode only; introspection through the SQLAlchemy inspector. |
81
+ | `helios_cortex.engines.trino_engine` | `TrinoEngine`, `TrinoOptions`, `TrinoIdentityError` | Provisional | `engine = "trino"`, options `host`, `port`, `catalog`, `schema`, `user`, `password` (basic authentication, https only), `http_scheme`, `verify`; needs the `[trino]` extra. All three identity modes: `service` (the service user), `impersonate` (authenticates as the service user, runs the session as the caller and checks `current_user`), `token` (the caller's JWT). |
82
+ | `helios_cortex.catalog` | `catalog_for`, `capabilities`, `GLOSSARY`, `LINEAGE`, `GlossaryCatalog`, `LineageCatalog`, `ColumnLineage`, `CatalogError` | Provisional | A catalog plugin (`helios.catalogs`) is a class whose `from_catalog(CatalogSettings)` builds it; `catalog_for` loads the one a `[catalogs.<name>]` entry names. Its capabilities are optional and detected from what it implements: `glossary` (`GlossaryCatalog`) and `lineage` (`LineageCatalog.column_lineage` → `ColumnLineage`); a plugin with neither is refused. Tables and columns come from the engine, so no catalog is needed to harvest. Atlas (helios-cloudera) offers both. Harvesting with a catalog is helios-model's (`helios_model.Catalog`, over `helios_model.harvest.TableHarvester`); the glossary is read with the catalog's own credentials, not the caller's. |
83
+ | `helios_cortex.llm` | `LLMClient`, `LLMError`, `LLMTimeoutError`, `llm_from_settings` | Stable | Needs the `[llm]` extra; without it, importing the module says so. |
84
+
85
+ ### Identity and plugins
86
+
87
+ | Module | Names | Level | Notes |
88
+ |---|---|---|---|
89
+ | `helios_cortex` | `IdentityMode`, `IdentityError` | Stable | `service`, `impersonate`, `token`; `IdentityError` (a `PermissionError`) when a caller lacks the username or token its mode needs. |
90
+ | `helios_cortex.data_authorization` | `DataPolicy`, `DataResource`, `DataAction`, `EngineIdentityAuthorizer`, `PlatformDataAuthorizer`, `PlatformDataDecision`, `DataRestriction`, `DataRestrictionDecision`, `DataAuthorizationDecision`, `DataAuthorizationDenied` | Stable | Platform enforcement plus deny-only host restrictions. The default platform authorizer allows query execution only when `DataResource.identity_mode` runs it as the caller. |
91
+ | `helios_cortex.visibility` | `ModelVisibility`, `AllModels`, `ModelNotVisible`, `visible_models`, `require_visible` | Stable | The host decides which models a caller may list and use; the default shows every model in the registry. A hidden model raises the same `LookupError` as a missing one. |
92
+ | `helios_cortex.audit_sink` | `AuditRecord`, `AuditSink`, `LoggingAuditSink`, `record_safely`, `sql_fingerprint` | Stable | Libraries emit `AuditRecord`s (action, outcome, model, caller username, row count, SQL fingerprint, error); the host's sink stores them. The default logs to `helios_cortex.audit_sink`. New optional fields are allowed. |
93
+ | `helios_cortex` | `Caller` | Stable | The host's caller: `username`, `token` (left out of `repr`, never logged) and free-form `attributes`, all optional. Helios authenticates no one; the engine's identity mode (2.2) decides which field it uses. |
94
+ | `helios_cortex.plugins` | `load`, `installed`, `names`, `missing_configured`, `require_configured`, `PluginInfo`, `PluginError`, `PluginNotFound`, `PluginLoadError`, `ENGINES`, `CATALOGS`, `STORES`, `TOOLS`, `COMMANDS`, `GROUPS` | Provisional | Entry-point groups `helios.engines`, `helios.catalogs`, `helios.stores`, `helios.tools`, `helios.commands`; the group names are Stable. What a plugin object must provide is set per group: engines in 3.5 (an `Engine` subclass with `from_connection`; see `helios_cortex.engines`), catalogs in 3.7 (a class with `from_catalog`; see `helios_cortex.catalog`), stores in 4.x, tools in 5.x, commands in 5.5. Settings that name a missing plugin still load; `require_configured` is the host's startup check. |
95
+
96
+ ## Internal
97
+
98
+ Everything not in a table above is internal. Most of it is leaving helios-cortex, which is
99
+ why it stays where it is instead of moving under `_internal/` and then moving again:
100
+
101
+ | Code | Where it goes |
102
+ |---|---|
103
+ | `ontology/parser.py`, the rest of `ontology/mapping.py`, `index/` modules other than the store and records | Reviewed for promotion when helios-crawl is built (Phase 10) |
104
+ | `_pending/` | helios-serve and helios-crawl; import-linter keeps other code from importing it |
105
+
106
+ ## Typing
107
+
108
+ helios-cortex ships `py.typed`. The type annotations on contract names are part of the contract.
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.5
2
+ Name: helios-cortex
3
+ Version: 0.1.0
4
+ Summary: Helios core: shared semantic query and data governance layer
5
+ Author: Helios Team
6
+ License-Expression: Apache-2.0
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: alembic>=1.13
9
+ Requires-Dist: duckdb>=1.3.0
10
+ Requires-Dist: jsonschema>=4.23.0
11
+ Requires-Dist: platformdirs>=4.0
12
+ Requires-Dist: pydantic-settings>=2.6
13
+ Requires-Dist: pydantic>=2.9.0
14
+ Requires-Dist: pyyaml>=6.0.0
15
+ Requires-Dist: sqlalchemy>=2.0
16
+ Requires-Dist: sqlglot>=27.0.0
17
+ Provides-Extra: azure
18
+ Requires-Dist: adlfs>=2024.4; extra == 'azure'
19
+ Provides-Extra: evidence
20
+ Requires-Dist: lancedb>=0.39.0; extra == 'evidence'
21
+ Requires-Dist: sentence-transformers>=6.1.0; extra == 'evidence'
22
+ Provides-Extra: gcs
23
+ Requires-Dist: gcsfs>=2024.6; extra == 'gcs'
24
+ Provides-Extra: llm
25
+ Requires-Dist: httpx>=0.27.0; extra == 'llm'
26
+ Provides-Extra: ontology
27
+ Requires-Dist: linkml-runtime>=1.8.0; extra == 'ontology'
28
+ Provides-Extra: ozone
29
+ Requires-Dist: fsspec>=2024.6; extra == 'ozone'
30
+ Requires-Dist: requests>=2.32; extra == 'ozone'
31
+ Provides-Extra: postgres
32
+ Requires-Dist: psycopg[binary]>=3.2; extra == 'postgres'
33
+ Provides-Extra: s3
34
+ Requires-Dist: s3fs>=2024.6; extra == 's3'
35
+ Provides-Extra: testing
36
+ Requires-Dist: pytest>=8.0; extra == 'testing'
37
+ Provides-Extra: trino
38
+ Requires-Dist: trino>=0.330; extra == 'trino'
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "helios-cortex"
7
+ version = "0.1.0"
8
+ description = "Helios core: shared semantic query and data governance layer"
9
+ authors = [{name = "Helios Team"}]
10
+ license = "Apache-2.0"
11
+ requires-python = ">=3.11"
12
+ # The base install: the compiler, semantic model, DuckDB, and the model registry on
13
+ # SQLite (SQLAlchemy and Alembic; the database itself is the standard library).
14
+ # Everything else is an extra; tests/test_optional_dependencies.py keeps it so.
15
+ dependencies = [
16
+ "duckdb>=1.3.0",
17
+ "jsonschema>=4.23.0",
18
+ "platformdirs>=4.0",
19
+ "pydantic>=2.9.0",
20
+ "pydantic-settings>=2.6",
21
+ "pyyaml>=6.0.0", # semantic model files and ontology mappings
22
+ "sqlglot>=27.0.0",
23
+ "sqlalchemy>=2.0", # the model registry (SQLite by default) and the sqlalchemy engine
24
+ "alembic>=1.13", # registry schema migrations
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ # LLM providers (helios_cortex.llm), used to propose models.
29
+ llm = [
30
+ "httpx>=0.27.0",
31
+ ]
32
+ # LinkML ontology parsing (helios_cortex.ontology.parser).
33
+ ontology = [
34
+ "linkml-runtime>=1.8.0",
35
+ ]
36
+ # Postgres for the registry (postgresql+psycopg://...) and the sqlalchemy engine. Other
37
+ # databases: install their SQLAlchemy driver (snowflake-sqlalchemy, mysqlclient, ...).
38
+ postgres = [
39
+ "psycopg[binary]>=3.2",
40
+ ]
41
+ # The trino engine plugin (all three identity modes).
42
+ trino = [
43
+ "trino>=0.330",
44
+ ]
45
+ # Object storage for the ArtifactStore (helios_cortex.artifact_storage), by URL scheme.
46
+ s3 = [
47
+ "s3fs>=2024.6", # s3:// (AWS S3, MinIO, Ozone's S3 gateway)
48
+ ]
49
+ azure = [
50
+ "adlfs>=2024.4", # abfs://, abfss://, az:// (ADLS Gen2, Blob storage)
51
+ ]
52
+ gcs = [
53
+ "gcsfs>=2024.6", # gs://
54
+ ]
55
+ ozone = [
56
+ "fsspec>=2024.6", # ofs://, through Ozone's HttpFS (WebHDFS) gateway
57
+ "requests>=2.32",
58
+ ]
59
+ # The backend conformance suite (helios_cortex.conformance), for testing a registry or
60
+ # storage backend.
61
+ testing = [
62
+ "pytest>=8.0",
63
+ ]
64
+ # Searching crawled documents. Without these, crawls run and evidence search
65
+ # reports that it is unavailable. sentence-transformers brings PyTorch.
66
+ evidence = [
67
+ "lancedb>=0.39.0",
68
+ "sentence-transformers>=6.1.0",
69
+ ]
70
+
71
+ # The built-in engine plugins. Impala and Atlas come with helios-cloudera
72
+ # (see helios_cortex.plugins). trino needs its extra; sqlalchemy needs the database's driver.
73
+ [project.entry-points."helios.engines"]
74
+ duckdb = "helios_cortex.engines.duckdb_engine:DuckDBEngine"
75
+ sqlalchemy = "helios_cortex.engines.sqlalchemy_engine:SqlAlchemyEngine"
76
+ trino = "helios_cortex.engines.trino_engine:TrinoEngine"
77
+
78
+ [tool.hatch.build.targets.wheel]
79
+ packages = ["src/helios_cortex"]
@@ -0,0 +1,18 @@
1
+ """helios core library: shared clients and logic for jobs and applications."""
2
+
3
+ from helios_cortex.caller import Caller, IdentityError, IdentityMode
4
+
5
+ __all__ = ["Caller", "IdentityError", "IdentityMode"]
6
+
7
+
8
+ def __getattr__(name: str) -> str:
9
+ # __version__ is read from the installed metadata on first use, so importing
10
+ # helios_cortex opens no files.
11
+ if name == "__version__":
12
+ from importlib.metadata import PackageNotFoundError, version
13
+
14
+ try:
15
+ return version("helios-cortex")
16
+ except PackageNotFoundError: # running from a source path without an install
17
+ return "0.0.0+unknown"
18
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,17 @@
1
+ """Optional dependencies: the base install is the compiler, semantic model, DuckDB and
2
+ SQLite; anything else comes with an extra (``pip install 'helios-cortex[llm]'``).
3
+
4
+ A module that needs an extra raises ``missing_extra(...)`` from the ImportError, so
5
+ the message says what to install instead of naming a package the user never chose.
6
+ Base modules import those modules only where they are used.
7
+ """
8
+ from __future__ import annotations
9
+
10
+
11
+ def missing_extra(feature: str, extra: str, error: ImportError) -> ModuleNotFoundError:
12
+ """The error for ``feature`` when the package ``error`` failed to import is missing."""
13
+ return ModuleNotFoundError(
14
+ f"{feature} needs the optional dependency {error.name or 'it imports'}: "
15
+ f"pip install 'helios-cortex[{extra}]'",
16
+ name=error.name,
17
+ )
@@ -0,0 +1,8 @@
1
+ """Feature code that core used to reach into, held here until its package exists.
2
+
3
+ - health: helios-serve (Phase 6)
4
+ - crawler, crawler_settings, evidence, browse: helios-crawl (Phase 10)
5
+
6
+ Nothing outside this directory in helios_cortex may import it; import-linter
7
+ enforces that (burndown 1.2). Each module leaves when its package is created.
8
+ """
@@ -0,0 +1,181 @@
1
+ """Browsing what the crawler found, from an ontology class down to passages
2
+ (CR-9): the entities of a class, then one entity's documents and the passages
3
+ that mention it.
4
+
5
+ Reads each source's latest successful crawl. A finished run never changes, so
6
+ its tables are kept in memory; runs that are no longer the latest are dropped.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import posixpath
12
+ from typing import Any
13
+
14
+ from helios_cortex._pending import evidence
15
+ from helios_cortex.index.records import AssetRecord, EntityRecord, MentionRecord, RelationshipRecord, SegmentRecord
16
+ from helios_cortex.index.store import IndexStore
17
+
18
+ ASSETS = "helios_index.assets"
19
+ SEGMENTS = "helios_index.segments"
20
+ MENTIONS = "helios_index.mentions"
21
+ ENTITY_LINKS = "helios_index.entity_links"
22
+ RELATIONSHIPS = "helios_index.relationships"
23
+ MAX_DOCUMENTS = 30
24
+
25
+ _tables: dict[tuple[str, str], list[Any]] = {}
26
+
27
+
28
+ def _rows(store: IndexStore, crawl_run_id: str, table: str) -> list[Any]:
29
+ key = (crawl_run_id, table)
30
+ if key not in _tables:
31
+ _tables[key] = store.read(table, {"crawl_run_id": crawl_run_id})
32
+ return _tables[key]
33
+
34
+
35
+ def _latest(store: IndexStore) -> list[Any]:
36
+ latest = evidence.latest_runs(store)
37
+ keep = {run.crawl_run_id for run in latest}
38
+ for key in [k for k in _tables if k[0] not in keep]:
39
+ del _tables[key]
40
+ return latest
41
+
42
+
43
+ def _entity_view(entity: EntityRecord, source: str) -> dict[str, Any]:
44
+ return {
45
+ "entity_id": entity.entity_id,
46
+ "class": entity.ontology_class,
47
+ "name": entity.canonical_name,
48
+ "keys": list(entity.external_ids),
49
+ "source": source,
50
+ "crawl_run_id": entity.crawl_run_id,
51
+ }
52
+
53
+
54
+ def class_entities(
55
+ store: IndexStore, classes: list[str], query: str = "", limit: int = 50
56
+ ) -> dict[str, Any]:
57
+ """Entities of the given ontology classes, most-documented first."""
58
+ wanted = {c.casefold() for c in classes}
59
+ needle = query.strip().casefold()
60
+ found: list[dict[str, Any]] = []
61
+ for run in _latest(store):
62
+ documents: dict[str, set[str]] = {}
63
+ for rel in _rows(store, run.crawl_run_id, RELATIONSHIPS):
64
+ if isinstance(rel, RelationshipRecord) and (rel.source_kind, rel.target_kind) == ("asset", "entity"):
65
+ documents.setdefault(rel.target_id, set()).add(rel.source_id)
66
+ for entity in _rows(store, run.crawl_run_id, evidence.ENTITIES):
67
+ if not isinstance(entity, EntityRecord) or entity.ontology_class.casefold() not in wanted:
68
+ continue
69
+ haystack = " ".join([entity.canonical_name, *entity.external_ids]).casefold()
70
+ if needle and needle not in haystack:
71
+ continue
72
+ found.append(
73
+ {**_entity_view(entity, run.source), "documents": len(documents.get(entity.entity_id, ()))}
74
+ )
75
+ found.sort(key=lambda e: (-e["documents"], e["name"]))
76
+ return {"total": len(found), "entities": found[: max(1, min(limit, 500))]}
77
+
78
+
79
+ def _document_name(asset: AssetRecord) -> str:
80
+ locator = asset.source_locator or {}
81
+ path = str(locator.get("key") or locator.get("uri") or locator.get("path") or "")
82
+ return posixpath.basename(path) or asset.asset_id
83
+
84
+
85
+ def entity_documents(store: IndexStore, crawl_run_id: str, entity_id: str) -> dict[str, Any] | None:
86
+ """One entity with its documents, the passages that mention it (with the
87
+ character ranges of each mention), related entities and claims."""
88
+ run = next((r for r in _latest(store) if r.crawl_run_id == crawl_run_id), None)
89
+ if run is None:
90
+ return None
91
+ entities = {
92
+ e.entity_id: e for e in _rows(store, crawl_run_id, evidence.ENTITIES) if isinstance(e, EntityRecord)
93
+ }
94
+ entity = entities.get(entity_id)
95
+ if entity is None:
96
+ return None
97
+
98
+ relation: dict[str, str] = {} # asset_id -> About or Mentions (About wins)
99
+ related: list[dict[str, Any]] = []
100
+ for rel in _rows(store, crawl_run_id, RELATIONSHIPS):
101
+ if not isinstance(rel, RelationshipRecord):
102
+ continue
103
+ if rel.source_kind == "asset" and rel.target_id == entity_id:
104
+ if relation.get(rel.source_id) != "About":
105
+ relation[rel.source_id] = rel.relationship_type
106
+ elif rel.source_kind == "entity" and entity_id in (rel.source_id, rel.target_id):
107
+ other = entities.get(rel.target_id if rel.source_id == entity_id else rel.source_id)
108
+ if other is not None:
109
+ related.append(
110
+ {
111
+ "relationship": rel.relationship_type,
112
+ "direction": "out" if rel.source_id == entity_id else "in",
113
+ "entity_id": other.entity_id,
114
+ "class": other.ontology_class,
115
+ "name": other.canonical_name,
116
+ }
117
+ )
118
+
119
+ links = {
120
+ link.mention_id: link
121
+ for link in store.read(ENTITY_LINKS, {"crawl_run_id": crawl_run_id, "entity_id": entity_id})
122
+ }
123
+ by_segment: dict[str, list[dict[str, Any]]] = {}
124
+ for mention in _rows(store, crawl_run_id, MENTIONS):
125
+ link = links.get(mention.mention_id) if isinstance(mention, MentionRecord) else None
126
+ if link is None:
127
+ continue
128
+ by_segment.setdefault(mention.segment_id, []).append(
129
+ {
130
+ "surface_form": mention.surface_form,
131
+ "start": mention.start_offset,
132
+ "end": mention.end_offset,
133
+ "resolved_by": link.resolved_by,
134
+ "link_type": link.link_type,
135
+ }
136
+ )
137
+ passages: dict[str, list[dict[str, Any]]] = {}
138
+ for segment in _rows(store, crawl_run_id, SEGMENTS):
139
+ if isinstance(segment, SegmentRecord) and segment.segment_id in by_segment:
140
+ passages.setdefault(segment.asset_id, []).append(
141
+ {
142
+ "segment_id": segment.segment_id,
143
+ "segment_type": segment.segment_type,
144
+ "ordinal": segment.ordinal,
145
+ "locator": segment.locator,
146
+ "text": segment.text,
147
+ "mentions": sorted(
148
+ by_segment[segment.segment_id], key=lambda m: (m["start"] is None, m["start"] or 0)
149
+ ),
150
+ }
151
+ )
152
+
153
+ documents = []
154
+ for asset in _rows(store, crawl_run_id, ASSETS):
155
+ if not isinstance(asset, AssetRecord):
156
+ continue
157
+ if asset.asset_id not in relation and asset.asset_id not in passages:
158
+ continue
159
+ documents.append(
160
+ {
161
+ "asset_id": asset.asset_id,
162
+ "name": _document_name(asset),
163
+ "class": asset.ontology_class,
164
+ "mime_type": asset.mime_type,
165
+ "timestamp": asset.semantic_timestamp,
166
+ "relationship": relation.get(asset.asset_id, "Mentions"),
167
+ "passages": sorted(passages.get(asset.asset_id, []), key=lambda p: p["ordinal"]),
168
+ }
169
+ )
170
+ documents.sort(
171
+ key=lambda d: (d["relationship"] != "About", -len(d["passages"]), d["timestamp"] or "", d["name"])
172
+ )
173
+ claims = evidence.entity_claims(store, entity_id)
174
+ return {
175
+ "entity": _entity_view(entity, run.source),
176
+ "documents_total": len(documents),
177
+ "documents": documents[:MAX_DOCUMENTS],
178
+ "related": sorted(related, key=lambda r: (r["relationship"], r["name"]))[:50],
179
+ "claims_total": claims["claims_total"],
180
+ "claims": claims["claims"],
181
+ }
@@ -0,0 +1,6 @@
1
+ """Shared crawler definitions: settings (CR-0e). The crawler itself runs as a
2
+ Workbench Job in the Helios project (apps/helios/crawler)."""
3
+
4
+ from helios_cortex._pending.crawler.settings import EMPTY_SETTINGS, CrawlerSettings, load_preset, ontology_problems, presets
5
+
6
+ __all__ = ["EMPTY_SETTINGS", "CrawlerSettings", "load_preset", "ontology_problems", "presets"]
@@ -0,0 +1,13 @@
1
+ # Shipped crawler settings presets. Each is <name>.yaml in this directory: a
2
+ # complete settings document a user can load, edit and save as a version.
3
+ presets:
4
+ retail-returns:
5
+ title: Retail returns
6
+ description: >-
7
+ Store returns over a TPC-DS warehouse: emails, chat threads and PDF return
8
+ reports. Identifier patterns for RMA, case and ticket numbers, the PDF
9
+ report's labels, and cue phrases for damage, return reason and refunds.
10
+
11
+ # Before settings schema 2 one set of rules was built into the crawler. A stored
12
+ # schema 1 document is read as this preset with the document's sections on top.
13
+ schema_1_defaults: retail-returns