helios-cortex 0.1.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.
- helios_cortex/__init__.py +18 -0
- helios_cortex/_optional.py +17 -0
- helios_cortex/_pending/__init__.py +8 -0
- helios_cortex/_pending/browse.py +181 -0
- helios_cortex/_pending/crawler/__init__.py +6 -0
- helios_cortex/_pending/crawler/presets/index.yaml +13 -0
- helios_cortex/_pending/crawler/presets/retail-returns.yaml +535 -0
- helios_cortex/_pending/crawler/settings.py +566 -0
- helios_cortex/_pending/crawler/sources.py +144 -0
- helios_cortex/_pending/crawler_settings.py +94 -0
- helios_cortex/_pending/evidence.py +416 -0
- helios_cortex/artifact_storage.py +289 -0
- helios_cortex/artifacts.py +376 -0
- helios_cortex/audit_sink.py +86 -0
- helios_cortex/caller.py +46 -0
- helios_cortex/catalog/__init__.py +21 -0
- helios_cortex/catalog/base.py +91 -0
- helios_cortex/compiler/__init__.py +9 -0
- helios_cortex/compiler/compiler.py +402 -0
- helios_cortex/conformance.py +364 -0
- helios_cortex/data_authorization.py +222 -0
- helios_cortex/domain.py +118 -0
- helios_cortex/engines/__init__.py +16 -0
- helios_cortex/engines/base.py +108 -0
- helios_cortex/engines/dialect.py +62 -0
- helios_cortex/engines/duckdb_engine.py +55 -0
- helios_cortex/engines/sqlalchemy_engine.py +121 -0
- helios_cortex/engines/trino_engine.py +132 -0
- helios_cortex/index/__init__.py +46 -0
- helios_cortex/index/__main__.py +35 -0
- helios_cortex/index/ids.py +63 -0
- helios_cortex/index/mappings.py +128 -0
- helios_cortex/index/ontology_versions.py +104 -0
- helios_cortex/index/records.py +300 -0
- helios_cortex/index/runs.py +102 -0
- helios_cortex/index/schema_impala.sql +201 -0
- helios_cortex/index/store.py +123 -0
- helios_cortex/index/tables.py +142 -0
- helios_cortex/llm/__init__.py +17 -0
- helios_cortex/llm/client.py +535 -0
- helios_cortex/ontology/__init__.py +35 -0
- helios_cortex/ontology/graph.py +209 -0
- helios_cortex/ontology/mapping.py +439 -0
- helios_cortex/ontology/parser.py +336 -0
- helios_cortex/ossie/__init__.py +2 -0
- helios_cortex/ossie/format.py +43 -0
- helios_cortex/ossie/model.py +263 -0
- helios_cortex/ossie/ossie-schema.json +367 -0
- helios_cortex/plugins.py +128 -0
- helios_cortex/py.typed +0 -0
- helios_cortex/registry/__init__.py +214 -0
- helios_cortex/registry/__main__.py +27 -0
- helios_cortex/registry/migrations/env.py +18 -0
- helios_cortex/registry/migrations/script.py.mako +20 -0
- helios_cortex/registry/migrations/versions/0001_registry.py +73 -0
- helios_cortex/registry/sql.py +312 -0
- helios_cortex/runs.py +517 -0
- helios_cortex/settings.py +355 -0
- helios_cortex/visibility.py +46 -0
- helios_cortex-0.1.0.dist-info/METADATA +38 -0
- helios_cortex-0.1.0.dist-info/RECORD +63 -0
- helios_cortex-0.1.0.dist-info/WHEEL +4 -0
- helios_cortex-0.1.0.dist-info/entry_points.txt +4 -0
|
@@ -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
|