querygraph 0.2.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.
- querygraph-0.2.0/.gitignore +4 -0
- querygraph-0.2.0/PKG-INFO +172 -0
- querygraph-0.2.0/README.md +142 -0
- querygraph-0.2.0/RELEASES.md +12 -0
- querygraph-0.2.0/examples/README.md +51 -0
- querygraph-0.2.0/examples/osi_semantic_croissant.py +58 -0
- querygraph-0.2.0/examples/pyspark_query_sail.py +26 -0
- querygraph-0.2.0/examples/typedid_langchain_agents.py +33 -0
- querygraph-0.2.0/pyproject.toml +45 -0
- querygraph-0.2.0/querygraph/__init__.py +14 -0
- querygraph-0.2.0/querygraph/__main__.py +4 -0
- querygraph-0.2.0/querygraph/agents.py +89 -0
- querygraph-0.2.0/querygraph/base58.py +15 -0
- querygraph-0.2.0/querygraph/cdif.py +205 -0
- querygraph-0.2.0/querygraph/cli.py +123 -0
- querygraph-0.2.0/querygraph/codata.py +38 -0
- querygraph-0.2.0/querygraph/croissant.py +86 -0
- querygraph-0.2.0/querygraph/dataverse.py +155 -0
- querygraph-0.2.0/querygraph/did.py +51 -0
- querygraph-0.2.0/querygraph/lakehouse.py +115 -0
- querygraph-0.2.0/querygraph/lineage.py +106 -0
- querygraph-0.2.0/querygraph/navigator.py +141 -0
- querygraph-0.2.0/querygraph/odrl.py +60 -0
- querygraph-0.2.0/querygraph/odrl_rights.py +50 -0
- querygraph-0.2.0/querygraph/osi.py +155 -0
- querygraph-0.2.0/querygraph/qglake.py +99 -0
- querygraph-0.2.0/querygraph/rbac.py +31 -0
- querygraph-0.2.0/querygraph/typedid.py +211 -0
- querygraph-0.2.0/querygraph/validation.py +41 -0
- querygraph-0.2.0/tests/test_python_ecosystem.py +244 -0
- querygraph-0.2.0/tests/test_python_semantics.py +47 -0
- querygraph-0.2.0/tests/test_rust_equivalence.py +39 -0
- querygraph-0.2.0/uv.lock +1376 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: querygraph
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python port of the QueryGraph AI Navigator semantic layer
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: pydantic>=2.7
|
|
8
|
+
Provides-Extra: agents
|
|
9
|
+
Requires-Dist: langchain-core>=0.3; extra == 'agents'
|
|
10
|
+
Provides-Extra: all
|
|
11
|
+
Requires-Dist: googleapis-common-protos>=1.75.0; extra == 'all'
|
|
12
|
+
Requires-Dist: grpcio-status>=1.81.0; extra == 'all'
|
|
13
|
+
Requires-Dist: grpcio>=1.81.0; extra == 'all'
|
|
14
|
+
Requires-Dist: langchain-core>=0.3; extra == 'all'
|
|
15
|
+
Requires-Dist: pandas>=3.0.0; extra == 'all'
|
|
16
|
+
Requires-Dist: pyarrow>=24.0.0; extra == 'all'
|
|
17
|
+
Requires-Dist: pyspark>=4.1.2; extra == 'all'
|
|
18
|
+
Requires-Dist: zstandard>=0.25.0; extra == 'all'
|
|
19
|
+
Provides-Extra: lakehouse
|
|
20
|
+
Requires-Dist: googleapis-common-protos>=1.75.0; extra == 'lakehouse'
|
|
21
|
+
Requires-Dist: grpcio-status>=1.81.0; extra == 'lakehouse'
|
|
22
|
+
Requires-Dist: grpcio>=1.81.0; extra == 'lakehouse'
|
|
23
|
+
Requires-Dist: pandas>=3.0.0; extra == 'lakehouse'
|
|
24
|
+
Requires-Dist: pyarrow>=24.0.0; extra == 'lakehouse'
|
|
25
|
+
Requires-Dist: pyspark>=4.1.2; extra == 'lakehouse'
|
|
26
|
+
Requires-Dist: zstandard>=0.25.0; extra == 'lakehouse'
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'test'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# QueryGraph Python
|
|
32
|
+
|
|
33
|
+
Python ecosystem for the QueryGraph AI Navigator.
|
|
34
|
+
|
|
35
|
+
It mirrors and extends the Rust implementation in `../qg-rust`:
|
|
36
|
+
|
|
37
|
+
- Croissant JSON-LD dataset metadata
|
|
38
|
+
- CDIF discovery/access/profile projection
|
|
39
|
+
- deterministic `did:oyd` identity documents
|
|
40
|
+
- ODRL permissions and prohibitions
|
|
41
|
+
- OSI semantic models over Croissant fields and Sail columns
|
|
42
|
+
- TypeDID agents modeled with Pydantic
|
|
43
|
+
- optional LangChain adapters for governed agent tools
|
|
44
|
+
- OpenLineage events and DID-style attestations
|
|
45
|
+
- PySpark helpers for querying a local Sail warehouse
|
|
46
|
+
- a CLI compatible with the Rust semantic bundle commands
|
|
47
|
+
|
|
48
|
+
The design goal is Python-native ergonomics over the same governed lakehouse:
|
|
49
|
+
Rust loads and verifies the warehouse; Python gives notebooks, PySpark users,
|
|
50
|
+
LangChain agents, and data scientists a typed interop layer.
|
|
51
|
+
|
|
52
|
+
## Stack versions
|
|
53
|
+
|
|
54
|
+
This port tracks the same coordinated QueryGraph stack releases as `../qg-rust`:
|
|
55
|
+
|
|
56
|
+
- **Grust 0.11.0 "Crab"** — the property-graph + GQL/Cypher substrate.
|
|
57
|
+
- **TypeSec 0.11.0 "Burano"** — the typed security fabric; the Pydantic
|
|
58
|
+
`TypeDidEnvelope` mirrors its audit-safe attestation (action, resource,
|
|
59
|
+
privacy level, negotiated profile, and an envelope digest).
|
|
60
|
+
- **LakeCat 0.2.1 "Lynx"** — the thin Iceberg REST catalog boundary, now sharing
|
|
61
|
+
its bootstrap-bundle wire format with the importer via `qglake-bundle`.
|
|
62
|
+
|
|
63
|
+
See `../qg-rust/docs/blog/announcing-querygraph-stack.md` for the full story.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
Core metadata and TypeDID/Pydantic support:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv sync
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Optional PySpark/Sail support:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uv sync --extra lakehouse
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Optional LangChain tool adapters:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv sync --extra agents
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Everything:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv sync --extra all
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Build a Semantic Bundle
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python -m querygraph navigator \
|
|
95
|
+
--dataset-name "Hazard vocabulary" \
|
|
96
|
+
--description "Controlled vocabulary with multilingual technical terms" \
|
|
97
|
+
--landing-page "https://querygraph.ai/datasets/hazards" \
|
|
98
|
+
--data-url "https://querygraph.ai/datasets/hazards.csv"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## QG Lakehouse Agent Story
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python -m querygraph qglake-story --pretty
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This produces a Pydantic TypeDID multi-agent run: supervisor, finance, energy,
|
|
108
|
+
mobility, climate-health, reference, restricted-data broker, synthesis,
|
|
109
|
+
OpenLineage, and DID attestation.
|
|
110
|
+
|
|
111
|
+
## Query Sail with PySpark
|
|
112
|
+
|
|
113
|
+
Start Sail from the Rust project after the lakehouse has been loaded:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
cd ../qg-rust
|
|
117
|
+
sail spark server --port 50051
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
In another shell:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cd ../qg-python
|
|
124
|
+
uv sync --extra lakehouse
|
|
125
|
+
uv run querygraph lakehouse-register \
|
|
126
|
+
--manifest ../qg-rust/.querygraph/lakehouse/manifest/load-report.json \
|
|
127
|
+
--warehouse ../qg-rust/spark-warehouse
|
|
128
|
+
uv run querygraph audit-register --warehouse ../qg-rust/spark-warehouse
|
|
129
|
+
uv run querygraph pyspark-examples
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Open a shell:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
uv run pyspark --remote sc://127.0.0.1:50051
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Then query the registered views:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
spark.sql("SELECT COUNT(*) FROM global_temp.government_finance__countydata").show()
|
|
142
|
+
spark.sql("SELECT quantity, value, unit FROM global_temp.codata_constants_2022__codata_constants_2022 LIMIT 5").show(truncate=False)
|
|
143
|
+
spark.sql("SELECT event_hash, event_type, job_name FROM global_temp.openlineage_events LIMIT 10").show(truncate=False)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## OSI with Semantic Croissant
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
uv run python examples/osi_semantic_croissant.py
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The example starts with concrete Semantic Croissant fields and projects them
|
|
153
|
+
into an OSI semantic model with ontology terms and Sail SQL expressions.
|
|
154
|
+
|
|
155
|
+
## TypeDID Agents with LangChain
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync --extra agents
|
|
159
|
+
uv run python examples/typedid_langchain_agents.py
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The agents are Pydantic models first. When LangChain is installed, a
|
|
163
|
+
`TypeDidLangChainToolAdapter` exposes the same governed agent as a LangChain
|
|
164
|
+
`StructuredTool`.
|
|
165
|
+
|
|
166
|
+
## Test
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv run python -m pytest
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The test suite includes equivalence checks against the sibling Rust implementation.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# QueryGraph Python
|
|
2
|
+
|
|
3
|
+
Python ecosystem for the QueryGraph AI Navigator.
|
|
4
|
+
|
|
5
|
+
It mirrors and extends the Rust implementation in `../qg-rust`:
|
|
6
|
+
|
|
7
|
+
- Croissant JSON-LD dataset metadata
|
|
8
|
+
- CDIF discovery/access/profile projection
|
|
9
|
+
- deterministic `did:oyd` identity documents
|
|
10
|
+
- ODRL permissions and prohibitions
|
|
11
|
+
- OSI semantic models over Croissant fields and Sail columns
|
|
12
|
+
- TypeDID agents modeled with Pydantic
|
|
13
|
+
- optional LangChain adapters for governed agent tools
|
|
14
|
+
- OpenLineage events and DID-style attestations
|
|
15
|
+
- PySpark helpers for querying a local Sail warehouse
|
|
16
|
+
- a CLI compatible with the Rust semantic bundle commands
|
|
17
|
+
|
|
18
|
+
The design goal is Python-native ergonomics over the same governed lakehouse:
|
|
19
|
+
Rust loads and verifies the warehouse; Python gives notebooks, PySpark users,
|
|
20
|
+
LangChain agents, and data scientists a typed interop layer.
|
|
21
|
+
|
|
22
|
+
## Stack versions
|
|
23
|
+
|
|
24
|
+
This port tracks the same coordinated QueryGraph stack releases as `../qg-rust`:
|
|
25
|
+
|
|
26
|
+
- **Grust 0.11.0 "Crab"** — the property-graph + GQL/Cypher substrate.
|
|
27
|
+
- **TypeSec 0.11.0 "Burano"** — the typed security fabric; the Pydantic
|
|
28
|
+
`TypeDidEnvelope` mirrors its audit-safe attestation (action, resource,
|
|
29
|
+
privacy level, negotiated profile, and an envelope digest).
|
|
30
|
+
- **LakeCat 0.2.1 "Lynx"** — the thin Iceberg REST catalog boundary, now sharing
|
|
31
|
+
its bootstrap-bundle wire format with the importer via `qglake-bundle`.
|
|
32
|
+
|
|
33
|
+
See `../qg-rust/docs/blog/announcing-querygraph-stack.md` for the full story.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
Core metadata and TypeDID/Pydantic support:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv sync
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Optional PySpark/Sail support:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv sync --extra lakehouse
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Optional LangChain tool adapters:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv sync --extra agents
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Everything:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv sync --extra all
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Build a Semantic Bundle
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python -m querygraph navigator \
|
|
65
|
+
--dataset-name "Hazard vocabulary" \
|
|
66
|
+
--description "Controlled vocabulary with multilingual technical terms" \
|
|
67
|
+
--landing-page "https://querygraph.ai/datasets/hazards" \
|
|
68
|
+
--data-url "https://querygraph.ai/datasets/hazards.csv"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## QG Lakehouse Agent Story
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m querygraph qglake-story --pretty
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This produces a Pydantic TypeDID multi-agent run: supervisor, finance, energy,
|
|
78
|
+
mobility, climate-health, reference, restricted-data broker, synthesis,
|
|
79
|
+
OpenLineage, and DID attestation.
|
|
80
|
+
|
|
81
|
+
## Query Sail with PySpark
|
|
82
|
+
|
|
83
|
+
Start Sail from the Rust project after the lakehouse has been loaded:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd ../qg-rust
|
|
87
|
+
sail spark server --port 50051
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
In another shell:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cd ../qg-python
|
|
94
|
+
uv sync --extra lakehouse
|
|
95
|
+
uv run querygraph lakehouse-register \
|
|
96
|
+
--manifest ../qg-rust/.querygraph/lakehouse/manifest/load-report.json \
|
|
97
|
+
--warehouse ../qg-rust/spark-warehouse
|
|
98
|
+
uv run querygraph audit-register --warehouse ../qg-rust/spark-warehouse
|
|
99
|
+
uv run querygraph pyspark-examples
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Open a shell:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv run pyspark --remote sc://127.0.0.1:50051
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then query the registered views:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
spark.sql("SELECT COUNT(*) FROM global_temp.government_finance__countydata").show()
|
|
112
|
+
spark.sql("SELECT quantity, value, unit FROM global_temp.codata_constants_2022__codata_constants_2022 LIMIT 5").show(truncate=False)
|
|
113
|
+
spark.sql("SELECT event_hash, event_type, job_name FROM global_temp.openlineage_events LIMIT 10").show(truncate=False)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## OSI with Semantic Croissant
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv run python examples/osi_semantic_croissant.py
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The example starts with concrete Semantic Croissant fields and projects them
|
|
123
|
+
into an OSI semantic model with ontology terms and Sail SQL expressions.
|
|
124
|
+
|
|
125
|
+
## TypeDID Agents with LangChain
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv sync --extra agents
|
|
129
|
+
uv run python examples/typedid_langchain_agents.py
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The agents are Pydantic models first. When LangChain is installed, a
|
|
133
|
+
`TypeDidLangChainToolAdapter` exposes the same governed agent as a LangChain
|
|
134
|
+
`StructuredTool`.
|
|
135
|
+
|
|
136
|
+
## Test
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv run python -m pytest
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The test suite includes equivalence checks against the sibling Rust implementation.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# QueryGraph Python releases
|
|
2
|
+
|
|
3
|
+
The Python port shares the version line and codenames of the Rust
|
|
4
|
+
implementation. The canonical release scheme and codename pool (birds of prey)
|
|
5
|
+
live in `../qg-rust/RELEASES.md`.
|
|
6
|
+
|
|
7
|
+
## Release log
|
|
8
|
+
|
|
9
|
+
| Version | Codename | Notes |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 0.2.0 | Peregrine | Tracks Grust 0.11.0 "Crab", TypeSec 0.11.0 "Burano", LakeCat 0.2.1 "Lynx". TypeDID attestation parity (privacy / profile / envelope digest) with the Rust port. |
|
|
12
|
+
| 0.1.0 | — | (pre-codename) Initial Python port. |
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# QueryGraph Python Examples
|
|
2
|
+
|
|
3
|
+
These examples turn the Python package into the notebook-facing ecosystem for
|
|
4
|
+
the Rust lakehouse.
|
|
5
|
+
|
|
6
|
+
## Sail and PySpark
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
sail spark server --port 50051
|
|
10
|
+
querygraph lakehouse-register \
|
|
11
|
+
--manifest ../qg-rust/.querygraph/lakehouse/manifest/load-report.json \
|
|
12
|
+
--warehouse ../qg-rust/spark-warehouse
|
|
13
|
+
querygraph audit-register --warehouse ../qg-rust/spark-warehouse
|
|
14
|
+
python examples/pyspark_query_sail.py
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Useful shell entry:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pyspark --remote sc://127.0.0.1:50051
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
spark.sql("SELECT COUNT(*) FROM global_temp.government_finance__countydata").show()
|
|
27
|
+
spark.sql("SELECT quantity, value, unit FROM global_temp.codata_constants_2022__codata_constants_2022 LIMIT 5").show(truncate=False)
|
|
28
|
+
spark.sql("SELECT event_hash, event_type, job_name FROM global_temp.openlineage_events LIMIT 10").show(truncate=False)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## OSI and Semantic Croissant
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python examples/osi_semantic_croissant.py
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The example starts with concrete Semantic Croissant fields and projects them
|
|
38
|
+
into an OSI model with business terms, Sail expressions, and ontology terms.
|
|
39
|
+
|
|
40
|
+
## TypeDID, Pydantic, and LangChain
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python examples/typedid_langchain_agents.py
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For the LangChain adapter path:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv sync --extra agents
|
|
50
|
+
python examples/typedid_langchain_agents.py
|
|
51
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Build OSI business semantics from a Semantic Croissant dataset."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from querygraph.croissant import CroissantDataset, Field, FileObject, RecordSet
|
|
8
|
+
from querygraph.osi import OsiDocument
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
dataset = CroissantDataset(
|
|
12
|
+
id="https://querygraph.ai/datasets/energy-burden/#dataset",
|
|
13
|
+
name="Energy Burden Demonstration",
|
|
14
|
+
description="Governed household energy survey fields in Sail.",
|
|
15
|
+
license="https://creativecommons.org/licenses/by/4.0/",
|
|
16
|
+
creators=["QueryGraph"],
|
|
17
|
+
files=[
|
|
18
|
+
FileObject(
|
|
19
|
+
id="https://querygraph.ai/datasets/energy-burden/#file",
|
|
20
|
+
name="energy_burden.parquet",
|
|
21
|
+
content_url="sail://qg_lakehouse/access_2018__access_data",
|
|
22
|
+
encoding_format="application/vnd.apache.parquet",
|
|
23
|
+
)
|
|
24
|
+
],
|
|
25
|
+
record_sets=[
|
|
26
|
+
RecordSet(
|
|
27
|
+
id="https://querygraph.ai/datasets/energy-burden/#recordset",
|
|
28
|
+
name="energy burden observations",
|
|
29
|
+
fields=[
|
|
30
|
+
Field(
|
|
31
|
+
"household_id",
|
|
32
|
+
"sc:Text",
|
|
33
|
+
"Stable household identifier.",
|
|
34
|
+
).semantic_type("https://schema.org/identifier"),
|
|
35
|
+
Field(
|
|
36
|
+
"energy_source",
|
|
37
|
+
"sc:Text",
|
|
38
|
+
"Primary household energy source.",
|
|
39
|
+
).semantic_type("https://querygraph.ai/ontology/energySource"),
|
|
40
|
+
Field(
|
|
41
|
+
"monthly_energy_cost",
|
|
42
|
+
"sc:Float",
|
|
43
|
+
"Monthly energy cost in local currency.",
|
|
44
|
+
).semantic_type("https://querygraph.ai/ontology/monthlyEnergyCost"),
|
|
45
|
+
],
|
|
46
|
+
)
|
|
47
|
+
],
|
|
48
|
+
keywords=["Semantic Croissant", "OSI", "energy burden", "Sail"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main() -> None:
|
|
53
|
+
osi = OsiDocument.from_croissant(dataset, sail_schema="qg_lakehouse")
|
|
54
|
+
print(json.dumps({"croissant": dataset.to_json_ld(), "osi": osi.to_json()}, indent=2))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Query the locally registered QueryGraph Sail warehouse with PySpark.
|
|
3
|
+
|
|
4
|
+
Start Sail first:
|
|
5
|
+
|
|
6
|
+
sail spark server --port 50051
|
|
7
|
+
|
|
8
|
+
Then register tables:
|
|
9
|
+
|
|
10
|
+
querygraph lakehouse-register --warehouse ../qg-rust/spark-warehouse \
|
|
11
|
+
--manifest ../qg-rust/.querygraph/lakehouse/manifest/load-report.json
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from querygraph.lakehouse import example_queries, spark_session
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main() -> None:
|
|
19
|
+
spark = spark_session("sc://127.0.0.1:50051")
|
|
20
|
+
for sql in example_queries("global_temp"):
|
|
21
|
+
print(f"\n-- {sql}")
|
|
22
|
+
spark.sql(sql).show(truncate=False)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""TypeDID agents with Pydantic models and an optional LangChain adapter."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from querygraph.agents import TypeDidLangChainToolAdapter, deterministic_specialist
|
|
8
|
+
from querygraph.qglake import build_python_qglake_story
|
|
9
|
+
from querygraph.typedid import TypeDidAgent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> None:
|
|
13
|
+
story = build_python_qglake_story()
|
|
14
|
+
print(json.dumps(story["synthesis"], indent=2))
|
|
15
|
+
|
|
16
|
+
finance = TypeDidAgent.new("FinanceAgent")
|
|
17
|
+
handler = deterministic_specialist(
|
|
18
|
+
finance,
|
|
19
|
+
summary="Fiscal capacity summary from governed Sail finance tables.",
|
|
20
|
+
evidence=["global_temp.government_finance__countydata"],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
tool = TypeDidLangChainToolAdapter(finance, handler).as_tool()
|
|
25
|
+
except RuntimeError as exc:
|
|
26
|
+
print(f"LangChain optional extra not installed: {exc}")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
print(tool.invoke({"question": "Summarize fiscal capacity", "resource": "compartment:finance"}))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "querygraph"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Python port of the QueryGraph AI Navigator semantic layer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"pydantic>=2.7",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
agents = [
|
|
18
|
+
"langchain-core>=0.3",
|
|
19
|
+
]
|
|
20
|
+
lakehouse = [
|
|
21
|
+
"googleapis-common-protos>=1.75.0",
|
|
22
|
+
"grpcio>=1.81.0",
|
|
23
|
+
"grpcio-status>=1.81.0",
|
|
24
|
+
"pandas>=3.0.0",
|
|
25
|
+
"pyarrow>=24.0.0",
|
|
26
|
+
"pyspark>=4.1.2",
|
|
27
|
+
"zstandard>=0.25.0",
|
|
28
|
+
]
|
|
29
|
+
test = ["pytest>=8.0"]
|
|
30
|
+
all = [
|
|
31
|
+
"googleapis-common-protos>=1.75.0",
|
|
32
|
+
"grpcio>=1.81.0",
|
|
33
|
+
"grpcio-status>=1.81.0",
|
|
34
|
+
"langchain-core>=0.3",
|
|
35
|
+
"pandas>=3.0.0",
|
|
36
|
+
"pyarrow>=24.0.0",
|
|
37
|
+
"pyspark>=4.1.2",
|
|
38
|
+
"zstandard>=0.25.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
querygraph = "querygraph.cli:main"
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from querygraph.navigator import AiNavigator, NavigatorInput, NavigatorOutput
|
|
2
|
+
from querygraph.osi import OsiDocument
|
|
3
|
+
from querygraph.typedid import TypeDidAgent, TypeDidEnvelope
|
|
4
|
+
from querygraph.odrl_rights import OdrlRightsLayer
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AiNavigator",
|
|
8
|
+
"NavigatorInput",
|
|
9
|
+
"OdrlRightsLayer",
|
|
10
|
+
"NavigatorOutput",
|
|
11
|
+
"OsiDocument",
|
|
12
|
+
"TypeDidAgent",
|
|
13
|
+
"TypeDidEnvelope",
|
|
14
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from querygraph.typedid import AgentResponse, GovernedPrompt, TypeDidAgent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TypeDidAgentRun(BaseModel):
|
|
11
|
+
supervisor: TypeDidAgent
|
|
12
|
+
specialists: list[TypeDidAgent]
|
|
13
|
+
prompt: GovernedPrompt
|
|
14
|
+
responses: list[AgentResponse] = Field(default_factory=list)
|
|
15
|
+
|
|
16
|
+
def aggregate(self) -> dict[str, Any]:
|
|
17
|
+
allowed = [response for response in self.responses if response.status == "allowed"]
|
|
18
|
+
denied = [response for response in self.responses if response.status == "denied"]
|
|
19
|
+
return {
|
|
20
|
+
"supervisor": self.supervisor.name,
|
|
21
|
+
"question": self.prompt.question,
|
|
22
|
+
"allowedSummaries": [response.summary for response in allowed],
|
|
23
|
+
"denials": [response.summary for response in denied],
|
|
24
|
+
"evidenceHashes": [
|
|
25
|
+
response.envelope.payload_sha256 for response in self.responses
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def deterministic_specialist(
|
|
31
|
+
agent: TypeDidAgent,
|
|
32
|
+
*,
|
|
33
|
+
summary: str,
|
|
34
|
+
status: str = "allowed",
|
|
35
|
+
evidence: list[str] | None = None,
|
|
36
|
+
redactions: list[str] | None = None,
|
|
37
|
+
) -> Callable[[dict[str, Any]], AgentResponse]:
|
|
38
|
+
def invoke(payload: dict[str, Any]) -> AgentResponse:
|
|
39
|
+
supervisor = TypeDidAgent.new("SupervisorAgent")
|
|
40
|
+
request = supervisor.request(
|
|
41
|
+
agent,
|
|
42
|
+
action=payload.get("action", "summarize"),
|
|
43
|
+
resource=payload.get("resource", "qg_lakehouse"),
|
|
44
|
+
payload=payload,
|
|
45
|
+
)
|
|
46
|
+
return agent.answer(
|
|
47
|
+
request,
|
|
48
|
+
status="allowed" if status == "allowed" else "denied",
|
|
49
|
+
summary=summary,
|
|
50
|
+
evidence=evidence or [payload.get("resource", "qg_lakehouse")],
|
|
51
|
+
redactions=redactions or [],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return invoke
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TypeDidLangChainToolAdapter:
|
|
58
|
+
"""Small adapter that exposes a TypeDID agent as a LangChain StructuredTool."""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
agent: TypeDidAgent,
|
|
63
|
+
handler: Callable[[dict[str, Any]], AgentResponse],
|
|
64
|
+
) -> None:
|
|
65
|
+
self.agent = agent
|
|
66
|
+
self.handler = handler
|
|
67
|
+
|
|
68
|
+
def as_tool(self):
|
|
69
|
+
try:
|
|
70
|
+
from langchain_core.tools import StructuredTool
|
|
71
|
+
except ImportError as exc: # pragma: no cover - depends on optional extra.
|
|
72
|
+
raise RuntimeError(
|
|
73
|
+
"Install querygraph[agents] to use LangChain tool adapters."
|
|
74
|
+
) from exc
|
|
75
|
+
|
|
76
|
+
def run(question: str, resource: str = "qg_lakehouse") -> dict[str, Any]:
|
|
77
|
+
response = self.handler(
|
|
78
|
+
{"question": question, "resource": resource, "action": "summarize"}
|
|
79
|
+
)
|
|
80
|
+
return response.model_dump(mode="json")
|
|
81
|
+
|
|
82
|
+
return StructuredTool.from_function(
|
|
83
|
+
func=run,
|
|
84
|
+
name=self.agent.name,
|
|
85
|
+
description=(
|
|
86
|
+
f"Governed TypeDID tool for {self.agent.name}; returns a signed "
|
|
87
|
+
"summary or denial."
|
|
88
|
+
),
|
|
89
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def b58encode(data: bytes) -> str:
|
|
5
|
+
if not data:
|
|
6
|
+
return ""
|
|
7
|
+
|
|
8
|
+
value = int.from_bytes(data, "big")
|
|
9
|
+
encoded = ""
|
|
10
|
+
while value:
|
|
11
|
+
value, remainder = divmod(value, 58)
|
|
12
|
+
encoded = ALPHABET[remainder] + encoded
|
|
13
|
+
|
|
14
|
+
leading_zeroes = len(data) - len(data.lstrip(b"\0"))
|
|
15
|
+
return "1" * leading_zeroes + encoded
|