querri 2.0.0__tar.gz → 2.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.
- {querri-2.0.0 → querri-2.1.0}/CHANGELOG.md +20 -0
- {querri-2.0.0 → querri-2.1.0}/PKG-INFO +2 -1
- {querri-2.0.0 → querri-2.1.0}/README.md +1 -0
- querri-2.1.0/docs/data-library.md +180 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_client.py +19 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_exceptions.py +21 -2
- querri-2.1.0/querri/_version.py +1 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/_app.py +6 -0
- querri-2.1.0/querri/cli/library.py +1763 -0
- querri-2.1.0/querri/resources/library.py +1180 -0
- querri-2.1.0/querri/types/library.py +355 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_cli.py +5 -3
- {querri-2.0.0 → querri-2.1.0}/tests/test_exceptions.py +34 -0
- querri-2.1.0/tests/test_library.py +842 -0
- querri-2.1.0/tests/test_library_streaming.py +325 -0
- querri-2.0.0/querri/_version.py +0 -1
- {querri-2.0.0 → querri-2.1.0}/.github/CONTRIBUTING.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/SECURITY.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/workflows/ci.yml +0 -0
- {querri-2.0.0 → querri-2.1.0}/.github/workflows/release.yml +0 -0
- {querri-2.0.0 → querri-2.1.0}/.gitignore +0 -0
- {querri-2.0.0 → querri-2.1.0}/CLAUDE.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/LICENSE +0 -0
- {querri-2.0.0 → querri-2.1.0}/TODO.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/docs/MIGRATION.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/docs/server-sdk.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/01_quick_start.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/02_user_management.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/03_access_policies.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/04_embed_session.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/05_get_session.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/06_projects.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/07_dashboards.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/08_chat_streaming.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/09_async_client.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/10_error_handling.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/11_user_scoped_client.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/12_data_operations.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/web-embed/index.html +0 -0
- {querri-2.0.0 → querri-2.1.0}/examples/web-embed/server.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/pyproject.toml +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/__init__.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_auth.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_base_client.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_config.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_convenience.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_pagination.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_streaming.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/_user_client.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/__init__.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/_context.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/_image.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/_output.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/audit.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/auth.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/chat.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/chats.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/dashboards.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/embed.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/files.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/keys.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/policies.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/projects.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/sharing.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/sources.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/steps.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/usage.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/users.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/views.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/cli/whoami.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/py.typed +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/__init__.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/audit.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/dashboards.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/embed.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/files.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/keys.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/policies.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/projects.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/sharing.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/sources.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/usage.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/users.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/resources/views.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/__init__.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/audit.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/chat.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/dashboard.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/data.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/embed.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/file.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/key.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/policy.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/project.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/sharing.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/usage.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/querri/types/user.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/scripts/release.sh +0 -0
- {querri-2.0.0 → querri-2.1.0}/skills/README.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/skills/querri-cli/SKILL.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/skills/querri-cli/references/json-shapes.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/skills/querri-cli/references/troubleshooting.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/__init__.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/conftest.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/rls-integration-results.md +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_cli_commands.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_client.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_config.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_convenience.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_integration.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_new_features.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_pagination.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_resources.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_rls_integration.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_security.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_streaming.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/tests/test_streaming_v2.py +0 -0
- {querri-2.0.0 → querri-2.1.0}/uv.lock +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.0 (2026-09-24)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`client.library`** (sync and async) and the `querri library` CLI: the Data
|
|
8
|
+
Library surface — libraries, collections, anchor and refining questions,
|
|
9
|
+
graph search and zoom, `record_fact`, the Librarian `chat` / `chat_stream`,
|
|
10
|
+
`ask`, `intake`, `consolidate`, `eval`, backfill and health. Reference:
|
|
11
|
+
[docs/data-library.md](docs/data-library.md). This is the `dev` line that
|
|
12
|
+
had been building since 1.1.0; it ships here unchanged.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Error bodies without a Stripe-style envelope keep their message and
|
|
17
|
+
code.** FastAPI raises `{"detail": {"error": {...}}}` (or a plain string
|
|
18
|
+
`detail`); `APIError.message`, `.code` and `.type` were left as
|
|
19
|
+
`"HTTP <status>"` / `None` for those, so a `NotFoundError` for a step with
|
|
20
|
+
no data read the same as one for a missing project. Both shapes are parsed
|
|
21
|
+
now.
|
|
22
|
+
|
|
3
23
|
## 2.0.0 (2026-08-31)
|
|
4
24
|
|
|
5
25
|
Migration guide with before/after snippets: [docs/MIGRATION.md](docs/MIGRATION.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: querri
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Python SDK and CLI for the Querri data analysis platform
|
|
5
5
|
Project-URL: Homepage, https://querri.com
|
|
6
6
|
Project-URL: Documentation, https://docs.querri.com/sdk/python
|
|
@@ -290,6 +290,7 @@ See **[docs/server-sdk.md](docs/server-sdk.md#user-scoped-client-as_user)** for
|
|
|
290
290
|
| `client.projects.chats` | Chats within projects | `create`, `list`, `get`, `stream`, `cancel`, `delete` |
|
|
291
291
|
| `client.sources` | Sources, connectors & data | `list`, `create`, `create_data_source`, `query`, `source_data`, `append_rows`, `replace_data`, `ask`, `sync`, `list_connectors` |
|
|
292
292
|
| `client.views` | SQL-defined views | `list`, `create`, `get`, `update`, `delete`, `run`, `get_run`, `wait_for_run`, `preview`, `chat` |
|
|
293
|
+
| `client.library` | Data Library — graph + librarian agent | `create_library`, `create_collection`, `create_anchor_question`, `create_refining_question`, `link`, `status`, `list_nodes`, `get_node`, `search`, `zoom`, `record_fact`, `chat`, `chat_stream`, `backfill`, `seed_fixture`, `health` |
|
|
293
294
|
| `client.files` | File management | `upload`, `list`, `get`, `delete` |
|
|
294
295
|
| `client.keys` | API key management | `create`, `list`, `get`, `delete` |
|
|
295
296
|
| `client.sharing` | Sharing & permissions | `share_project`, `share_dashboard`, `share_source`, `list_project_shares`, `list_dashboard_shares`, `revoke_project_share`, `revoke_dashboard_share`, `org_share_source` |
|
|
@@ -244,6 +244,7 @@ See **[docs/server-sdk.md](docs/server-sdk.md#user-scoped-client-as_user)** for
|
|
|
244
244
|
| `client.projects.chats` | Chats within projects | `create`, `list`, `get`, `stream`, `cancel`, `delete` |
|
|
245
245
|
| `client.sources` | Sources, connectors & data | `list`, `create`, `create_data_source`, `query`, `source_data`, `append_rows`, `replace_data`, `ask`, `sync`, `list_connectors` |
|
|
246
246
|
| `client.views` | SQL-defined views | `list`, `create`, `get`, `update`, `delete`, `run`, `get_run`, `wait_for_run`, `preview`, `chat` |
|
|
247
|
+
| `client.library` | Data Library — graph + librarian agent | `create_library`, `create_collection`, `create_anchor_question`, `create_refining_question`, `link`, `status`, `list_nodes`, `get_node`, `search`, `zoom`, `record_fact`, `chat`, `chat_stream`, `backfill`, `seed_fixture`, `health` |
|
|
247
248
|
| `client.files` | File management | `upload`, `list`, `get`, `delete` |
|
|
248
249
|
| `client.keys` | API key management | `create`, `list`, `get`, `delete` |
|
|
249
250
|
| `client.sharing` | Sharing & permissions | `share_project`, `share_dashboard`, `share_source`, `list_project_shares`, `list_dashboard_shares`, `revoke_project_share`, `revoke_dashboard_share`, `org_share_source` |
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Data Library — CLI + SDK reference
|
|
2
|
+
|
|
3
|
+
The Data Library is Querri's knowledge-graph + semantic-search layer over Collections, Questions, Sources, Views, KPIs, and Facts. The `querri library` CLI subcommand + `client.library` SDK resource expose the full surface.
|
|
4
|
+
|
|
5
|
+
This doc is the user-facing reference. For module-level architecture see [`~/Q/Querri/server-api/querri_core/library/MODULE.md`](https://github.com/Querri-inc/server-api/blob/feat/data-overview/server-api/querri_core/library/MODULE.md) and the program-level [`documentation/data-library/`](https://github.com/Querri-inc/server-api/tree/feat/data-overview/documentation/data-library).
|
|
6
|
+
|
|
7
|
+
## Auth + setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
export QUERRI_API_KEY=qk_...
|
|
11
|
+
export QUERRI_ORG_ID=<tenant-id> # required for API-key auth
|
|
12
|
+
export QUERRI_HOST=http://localhost # dev; production defaults to https://app.querri.com
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Most commands take `--library-id` explicitly. To save typing, set an active library once and subsequent commands pick it up:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
querri library use lib_<tenant-id>_<uuid>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The active library is stored in `~/.querri/library_context.json`. Override per-call with `--library-id` or `QUERRI_LIBRARY_ID`.
|
|
22
|
+
|
|
23
|
+
## Quick start — the demo path
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Create a library and select it.
|
|
27
|
+
querri library create-library "Primary Library"
|
|
28
|
+
querri library use <library-id-printed-above>
|
|
29
|
+
|
|
30
|
+
# 2. Seed it with realistic demo data (5 collections, 16 sources, ~150 series).
|
|
31
|
+
querri library seed-demo --fixture demo-acme
|
|
32
|
+
|
|
33
|
+
# 3. Inspect.
|
|
34
|
+
querri library status
|
|
35
|
+
querri library list Collection
|
|
36
|
+
querri library list ViewStub
|
|
37
|
+
|
|
38
|
+
# 4. Search semantically.
|
|
39
|
+
querri library search "customer churn signals"
|
|
40
|
+
querri library zoom "marketing attribution by channel" --zoom 50 --explain
|
|
41
|
+
|
|
42
|
+
# 5. Chat with the Librarian agent.
|
|
43
|
+
querri library chat "What collections relate to revenue?" --new --show-tools
|
|
44
|
+
|
|
45
|
+
# 6. Record a Fact (CLI-direct OR via the agent).
|
|
46
|
+
querri library record-fact \
|
|
47
|
+
"orders.status enum gained 'awaiting_carrier' in Mar 2026" \
|
|
48
|
+
--attach <source-node-id> --kind data_quality
|
|
49
|
+
|
|
50
|
+
# 7. Have the agent commission a new view.
|
|
51
|
+
querri library chat \
|
|
52
|
+
"Commission a view called orders_by_country that sums total_amount grouped by shipping_country, using the orders source. Build it." \
|
|
53
|
+
--show-tools
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI reference
|
|
57
|
+
|
|
58
|
+
All commands accept `--json` (a top-level flag, e.g. `querri --json library status`) for scripting.
|
|
59
|
+
|
|
60
|
+
### Workspace state
|
|
61
|
+
|
|
62
|
+
| Command | What it does |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `querri library use <library-id>` | Set active library (persists across invocations). |
|
|
65
|
+
| `querri library status [--library-id <id>]` | Counts per node_kind + tenant + total. |
|
|
66
|
+
| `querri library health` | Tenant-scoped health probe (Mongo doc count, embedding model). |
|
|
67
|
+
|
|
68
|
+
### Create
|
|
69
|
+
|
|
70
|
+
| Command | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `querri library create-library <name> [--summary <s>]` | New `Library` node. |
|
|
73
|
+
| `querri library create-collection <name> [--anchor "<text>"] [--summary <s>]` | New `Collection`. With `--anchor` also mints an `AnchorQuestion` + the `anchor_of` edge in one call. |
|
|
74
|
+
| `querri library create-question <text> [--refining] [--name <n>]` | Anchor question by default; pass `--refining` for a `RefiningQuestion`. |
|
|
75
|
+
| `querri library add-refining <anchor-id> "<text>"` | New `RefiningQuestion` linked to the anchor via `refines`. |
|
|
76
|
+
| `querri library link <a> <b> <relation> [--weight X] [--confidence X]` | Bidirectional edge between two existing nodes. `<relation>` is one of `contains`, `anchor_of`, `refines`, `parent_of`, `in_category`, `uses_source`, `uses_view`, `connector_provides`, `view_built_from`, `series_in`, `about`, `semantic_neighbor`, `mentions`. |
|
|
77
|
+
|
|
78
|
+
### Read
|
|
79
|
+
|
|
80
|
+
| Command | What it does |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `querri library get <node-id>` | Fetch a single node by id. |
|
|
83
|
+
| `querri library list <node-kind> [--limit N]` | All nodes of a given kind in the active library. |
|
|
84
|
+
| `querri library search <query> [--kind K] [--limit N] [--surface S]` | Vector ANN search — flat top-K, no graph traversal. Fast surface. |
|
|
85
|
+
| `querri library zoom <query> [--zoom N] [--budget N] [--top-k N] [--threshold X] [--kind K] [--explain]` | Vector-seeded multi-focal graph zoom: ANN-resolves focals, BFS up to 2 hops, scores by `edge.confidence × edge.weight × (zoom/100)^hops`. `--explain` prints per-stage latency + algorithm decisions. |
|
|
86
|
+
| `querri library zoom --focal <id> [--focal <id>] [--zoom N]` | Skip ANN; zoom directly from given focal IDs (for iterative drill-in). |
|
|
87
|
+
|
|
88
|
+
### Write — facts + view commissioning
|
|
89
|
+
|
|
90
|
+
| Command | What it does |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `querri library record-fact "<statement>" --attach <node-id> [--attach <id>] [--kind <K>] [--evidence <url>] [--confidence X]` | Create a `Fact` node + `about` edges to each `--attach` target. `--kind` is one of `contextual_note` (default), `data_quality`, `timing`, `scope_constraint`. |
|
|
93
|
+
| `querri library chat "<message>" [--new] [--chat-id <id>] [--show-tools]` | Streaming chat with the LibrarianAgent. Three tools available to the agent: `search_graph`, `record_fact`, `commission_view`. `--show-tools` prints tool calls + Views-agent progress dots in real time. Active chat id persists per-library so subsequent calls continue the same conversation; `--new` starts fresh. |
|
|
94
|
+
|
|
95
|
+
### Bulk / admin
|
|
96
|
+
|
|
97
|
+
| Command | What it does |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `querri library seed-demo [--fixture <name>]` | Apply a named demo fixture (currently: `demo-acme`). Writes both library_graph stubs AND real legacy `sources` + `connectors` docs so the Views agent can resolve fixture sources. Idempotent. |
|
|
100
|
+
| `querri library backfill [--library-id <id>] [--no-series]` | Walk the tenant's existing `connectors` + `sources` collections and create matching stubs in `library_graph`. Idempotent. |
|
|
101
|
+
|
|
102
|
+
## SDK reference
|
|
103
|
+
|
|
104
|
+
Sync (`client.library`) and async (`client.library` on `AsyncQuerri`) have identical surfaces.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from querri import Querri
|
|
108
|
+
|
|
109
|
+
client = Querri(api_key="qk_...", org_id="<tenant>", host="http://localhost")
|
|
110
|
+
|
|
111
|
+
# Workspace
|
|
112
|
+
status = client.library.status(library_id="lib_...")
|
|
113
|
+
print(status.counts_by_kind) # {"Collection": 8, "Fact": 4, ...}
|
|
114
|
+
|
|
115
|
+
# Search
|
|
116
|
+
hits = client.library.search(query="churn risk", library_id="lib_...", limit=5)
|
|
117
|
+
for h in hits.results:
|
|
118
|
+
print(h.score, h.node_kind, h.name)
|
|
119
|
+
|
|
120
|
+
# Vector-seeded graph zoom
|
|
121
|
+
zoom = client.library.zoom(
|
|
122
|
+
library_id="lib_...",
|
|
123
|
+
query="channel attribution and ROI",
|
|
124
|
+
zoom=50, budget_tokens=2000, top_k_focal=4,
|
|
125
|
+
)
|
|
126
|
+
for f in zoom.focal_nodes:
|
|
127
|
+
print(f.score, f.confidence_tier, f.node_kind, f.name)
|
|
128
|
+
for n in zoom.subgraph_nodes:
|
|
129
|
+
print(n.edge_strength, n.hops, "/".join(n.edge_path), n.name)
|
|
130
|
+
|
|
131
|
+
# Record a fact via the API directly
|
|
132
|
+
fact = client.library.record_fact(
|
|
133
|
+
library_id="lib_...",
|
|
134
|
+
statement="orders.status enum gained 'awaiting_carrier' in Mar 2026",
|
|
135
|
+
fact_kind="data_quality",
|
|
136
|
+
source_node_ids=["src_..."],
|
|
137
|
+
evidence_refs=["https://runbooks/orders-status-evolution"],
|
|
138
|
+
confidence=0.95,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Chat — drained ChatResponse
|
|
142
|
+
result = client.library.chat(
|
|
143
|
+
library_id="lib_...",
|
|
144
|
+
message="What collections relate to revenue?",
|
|
145
|
+
chat_id=None, # omit to start fresh
|
|
146
|
+
)
|
|
147
|
+
print(result.assistant_message)
|
|
148
|
+
print(result.tool_calls) # list of {name, input, result, duration_ms}
|
|
149
|
+
|
|
150
|
+
# Chat — streaming events (sync iterator)
|
|
151
|
+
for event in client.library.chat_stream(
|
|
152
|
+
library_id="lib_...",
|
|
153
|
+
message="Commission a payment_failures view from the payments source",
|
|
154
|
+
):
|
|
155
|
+
print(event["type"], event.get("name") or event.get("text", "")[:80])
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Streaming event types
|
|
159
|
+
|
|
160
|
+
The chat stream yields these `type`s (in order, typically):
|
|
161
|
+
|
|
162
|
+
| Type | Payload |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `tool_use` | `{tool_use_id, name, input}` — emitted right before each tool call |
|
|
165
|
+
| `tool_progress` | `{tool_use_id, raw}` — pass-through inner-agent SSE chunks during `commission_view` |
|
|
166
|
+
| `tool_result` | `{tool_use_id, name, input, result, duration_ms}` |
|
|
167
|
+
| `assistant_text` | `{text}` — terminal LLM text in the final turn |
|
|
168
|
+
| `done` | `{chat_id, library_id, assistant_message, turns_used, stop_reason, input_tokens, output_tokens, total_ms}` |
|
|
169
|
+
| `error` | `{message}` — stream-level failure |
|
|
170
|
+
|
|
171
|
+
## Scopes
|
|
172
|
+
|
|
173
|
+
Library routes require API-key scopes:
|
|
174
|
+
|
|
175
|
+
| Scope | Routes |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `admin:library:read` | GET `/library/nodes`, `/library/nodes/{id}`, `/library/status`, `/library/health`, POST `/library/search`, `/library/zoom` |
|
|
178
|
+
| `admin:library:write` | POST `/library/libraries`, `/library/collections`, `/library/questions/*`, `/library/edges`, `/library/facts`, `/library/backfill`, `/library/seed-fixture`, `/library/chat` |
|
|
179
|
+
|
|
180
|
+
The wildcard `*` superadmin scope satisfies both. JWT auth gives both scopes to `member` role.
|
|
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from .resources.embed import AsyncEmbed, Embed
|
|
15
15
|
from .resources.files import AsyncFiles, Files
|
|
16
16
|
from .resources.keys import AsyncKeys, Keys
|
|
17
|
+
from .resources.library import AsyncLibrary, Library
|
|
17
18
|
from .resources.policies import AsyncPolicies, Policies
|
|
18
19
|
from .resources.projects import AsyncProjects, Projects
|
|
19
20
|
from .resources.sharing import AsyncSharing, Sharing
|
|
@@ -87,6 +88,7 @@ class Querri:
|
|
|
87
88
|
self._sharing: object | None = None
|
|
88
89
|
self._usage: object | None = None
|
|
89
90
|
self._audit: object | None = None
|
|
91
|
+
self._library: object | None = None
|
|
90
92
|
|
|
91
93
|
@property
|
|
92
94
|
def users(self) -> "Users":
|
|
@@ -184,6 +186,14 @@ class Querri:
|
|
|
184
186
|
self._audit = Audit(self._http)
|
|
185
187
|
return self._audit # type: ignore[return-value]
|
|
186
188
|
|
|
189
|
+
@property
|
|
190
|
+
def library(self) -> "Library":
|
|
191
|
+
if self._library is None:
|
|
192
|
+
from .resources.library import Library
|
|
193
|
+
|
|
194
|
+
self._library = Library(self._http)
|
|
195
|
+
return self._library # type: ignore[return-value]
|
|
196
|
+
|
|
187
197
|
def as_user(self, session: dict[str, Any]) -> "UserQuerri":
|
|
188
198
|
"""Create a user-scoped client from a ``get_session()`` result.
|
|
189
199
|
|
|
@@ -270,6 +280,7 @@ class AsyncQuerri:
|
|
|
270
280
|
self._sharing: object | None = None
|
|
271
281
|
self._usage: object | None = None
|
|
272
282
|
self._audit: object | None = None
|
|
283
|
+
self._library: object | None = None
|
|
273
284
|
|
|
274
285
|
@property
|
|
275
286
|
def users(self) -> "AsyncUsers":
|
|
@@ -367,6 +378,14 @@ class AsyncQuerri:
|
|
|
367
378
|
self._audit = AsyncAudit(self._http)
|
|
368
379
|
return self._audit # type: ignore[return-value]
|
|
369
380
|
|
|
381
|
+
@property
|
|
382
|
+
def library(self) -> "AsyncLibrary":
|
|
383
|
+
if self._library is None:
|
|
384
|
+
from .resources.library import AsyncLibrary
|
|
385
|
+
|
|
386
|
+
self._library = AsyncLibrary(self._http)
|
|
387
|
+
return self._library # type: ignore[return-value]
|
|
388
|
+
|
|
370
389
|
def as_user(self, session: dict[str, Any]) -> "AsyncUserQuerri":
|
|
371
390
|
"""Create a user-scoped async client from a ``get_session()`` result.
|
|
372
391
|
|
|
@@ -163,16 +163,35 @@ def raise_for_status(
|
|
|
163
163
|
retry_after: Retry-After value from response headers.
|
|
164
164
|
"""
|
|
165
165
|
error = body.get("error", {})
|
|
166
|
-
if isinstance(error, dict):
|
|
166
|
+
if isinstance(error, dict) and error:
|
|
167
167
|
err_type = str(error.get("type", ""))
|
|
168
168
|
err_code = str(error.get("code", ""))
|
|
169
169
|
err_message = str(error.get("message", f"HTTP {status}"))
|
|
170
170
|
err_doc_url = error.get("doc_url")
|
|
171
171
|
else:
|
|
172
|
+
# FastAPI's `HTTPException(detail=...)` produces `{"detail": ...}`
|
|
173
|
+
# with no `error` envelope. Prefer the `detail` payload over a
|
|
174
|
+
# generic "HTTP {status}" so admin-gate / validation errors carry
|
|
175
|
+
# the helpful server-side message through to the CLI user.
|
|
176
|
+
# `detail` may be a string OR a nested dict (FastAPI lets you raise
|
|
177
|
+
# an HTTPException with a structured detail).
|
|
178
|
+
detail = body.get("detail")
|
|
172
179
|
err_type = ""
|
|
173
180
|
err_code = ""
|
|
174
|
-
err_message = str(body) if body else f"HTTP {status}"
|
|
175
181
|
err_doc_url = None
|
|
182
|
+
if isinstance(detail, str) and detail:
|
|
183
|
+
err_message = detail
|
|
184
|
+
elif isinstance(detail, dict) and detail:
|
|
185
|
+
nested_error = detail.get("error")
|
|
186
|
+
if isinstance(nested_error, dict) and nested_error:
|
|
187
|
+
err_type = str(nested_error.get("type", ""))
|
|
188
|
+
err_code = str(nested_error.get("code", ""))
|
|
189
|
+
err_message = str(nested_error.get("message", f"HTTP {status}"))
|
|
190
|
+
err_doc_url = nested_error.get("doc_url")
|
|
191
|
+
else:
|
|
192
|
+
err_message = str(detail)
|
|
193
|
+
else:
|
|
194
|
+
err_message = str(body) if body else f"HTTP {status}"
|
|
176
195
|
|
|
177
196
|
exc_class = _STATUS_MAP.get(status, APIError)
|
|
178
197
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.0"
|
|
@@ -14,6 +14,7 @@ from querri.cli.dashboards import dashboards_app
|
|
|
14
14
|
from querri.cli.embed import embed_app
|
|
15
15
|
from querri.cli.files import files_app
|
|
16
16
|
from querri.cli.keys import keys_app
|
|
17
|
+
from querri.cli.library import library_app
|
|
17
18
|
from querri.cli.policies import policies_app
|
|
18
19
|
from querri.cli.projects import projects_app
|
|
19
20
|
from querri.cli.sharing import sharing_app
|
|
@@ -227,6 +228,11 @@ main_app.add_typer(
|
|
|
227
228
|
name="dashboard",
|
|
228
229
|
rich_help_panel="[#f15a24]Projects & Data[/#f15a24]",
|
|
229
230
|
)
|
|
231
|
+
main_app.add_typer(
|
|
232
|
+
library_app,
|
|
233
|
+
name="library",
|
|
234
|
+
rich_help_panel="[#f15a24]Projects & Data[/#f15a24]",
|
|
235
|
+
)
|
|
230
236
|
|
|
231
237
|
# ── Administration ──────────────────────────────────────────────────────
|
|
232
238
|
main_app.add_typer(
|