querri 1.0.2__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 (108) hide show
  1. querri-1.0.2/.github/CONTRIBUTING.md +60 -0
  2. querri-1.0.2/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  3. querri-1.0.2/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  4. querri-1.0.2/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  5. querri-1.0.2/.github/SECURITY.md +31 -0
  6. querri-1.0.2/.github/workflows/ci.yml +25 -0
  7. querri-1.0.2/.github/workflows/release.yml +42 -0
  8. querri-1.0.2/.gitignore +34 -0
  9. querri-1.0.2/CLAUDE.md +93 -0
  10. querri-1.0.2/PKG-INFO +310 -0
  11. querri-1.0.2/README.md +265 -0
  12. querri-1.0.2/docs/server-sdk.md +1704 -0
  13. querri-1.0.2/examples/01_quick_start.py +36 -0
  14. querri-1.0.2/examples/02_user_management.py +88 -0
  15. querri-1.0.2/examples/03_access_policies.py +122 -0
  16. querri-1.0.2/examples/04_embed_session.py +74 -0
  17. querri-1.0.2/examples/05_get_session.py +131 -0
  18. querri-1.0.2/examples/06_projects.py +109 -0
  19. querri-1.0.2/examples/07_dashboards.py +75 -0
  20. querri-1.0.2/examples/08_chat_streaming.py +82 -0
  21. querri-1.0.2/examples/09_async_client.py +98 -0
  22. querri-1.0.2/examples/10_error_handling.py +105 -0
  23. querri-1.0.2/examples/11_user_scoped_client.py +95 -0
  24. querri-1.0.2/examples/12_data_operations.py +112 -0
  25. querri-1.0.2/examples/web-embed/index.html +344 -0
  26. querri-1.0.2/examples/web-embed/server.py +451 -0
  27. querri-1.0.2/pyproject.toml +83 -0
  28. querri-1.0.2/querri/__init__.py +51 -0
  29. querri-1.0.2/querri/_auth.py +818 -0
  30. querri-1.0.2/querri/_base_client.py +379 -0
  31. querri-1.0.2/querri/_client.py +395 -0
  32. querri-1.0.2/querri/_config.py +140 -0
  33. querri-1.0.2/querri/_convenience.py +538 -0
  34. querri-1.0.2/querri/_exceptions.py +180 -0
  35. querri-1.0.2/querri/_pagination.py +278 -0
  36. querri-1.0.2/querri/_streaming.py +663 -0
  37. querri-1.0.2/querri/_user_client.py +193 -0
  38. querri-1.0.2/querri/_version.py +1 -0
  39. querri-1.0.2/querri/cli/__init__.py +21 -0
  40. querri-1.0.2/querri/cli/_app.py +246 -0
  41. querri-1.0.2/querri/cli/_context.py +216 -0
  42. querri-1.0.2/querri/cli/_image.py +234 -0
  43. querri-1.0.2/querri/cli/_output.py +293 -0
  44. querri-1.0.2/querri/cli/audit.py +68 -0
  45. querri-1.0.2/querri/cli/auth.py +556 -0
  46. querri-1.0.2/querri/cli/chat.py +1255 -0
  47. querri-1.0.2/querri/cli/chats.py +358 -0
  48. querri-1.0.2/querri/cli/dashboards.py +294 -0
  49. querri-1.0.2/querri/cli/embed.py +220 -0
  50. querri-1.0.2/querri/cli/files.py +214 -0
  51. querri-1.0.2/querri/cli/keys.py +304 -0
  52. querri-1.0.2/querri/cli/policies.py +424 -0
  53. querri-1.0.2/querri/cli/projects.py +916 -0
  54. querri-1.0.2/querri/cli/sharing.py +403 -0
  55. querri-1.0.2/querri/cli/sources.py +532 -0
  56. querri-1.0.2/querri/cli/steps.py +123 -0
  57. querri-1.0.2/querri/cli/usage.py +99 -0
  58. querri-1.0.2/querri/cli/users.py +223 -0
  59. querri-1.0.2/querri/cli/views.py +449 -0
  60. querri-1.0.2/querri/cli/whoami.py +78 -0
  61. querri-1.0.2/querri/py.typed +0 -0
  62. querri-1.0.2/querri/resources/__init__.py +1 -0
  63. querri-1.0.2/querri/resources/audit.py +119 -0
  64. querri-1.0.2/querri/resources/dashboards.py +295 -0
  65. querri-1.0.2/querri/resources/embed.py +268 -0
  66. querri-1.0.2/querri/resources/files.py +145 -0
  67. querri-1.0.2/querri/resources/keys.py +191 -0
  68. querri-1.0.2/querri/resources/policies.py +492 -0
  69. querri-1.0.2/querri/resources/projects.py +808 -0
  70. querri-1.0.2/querri/resources/sharing.py +330 -0
  71. querri-1.0.2/querri/resources/sources.py +511 -0
  72. querri-1.0.2/querri/resources/usage.py +106 -0
  73. querri-1.0.2/querri/resources/users.py +303 -0
  74. querri-1.0.2/querri/resources/views.py +347 -0
  75. querri-1.0.2/querri/types/__init__.py +104 -0
  76. querri-1.0.2/querri/types/audit.py +21 -0
  77. querri-1.0.2/querri/types/chat.py +51 -0
  78. querri-1.0.2/querri/types/dashboard.py +51 -0
  79. querri-1.0.2/querri/types/data.py +61 -0
  80. querri-1.0.2/querri/types/embed.py +38 -0
  81. querri-1.0.2/querri/types/file.py +18 -0
  82. querri-1.0.2/querri/types/key.py +36 -0
  83. querri-1.0.2/querri/types/policy.py +105 -0
  84. querri-1.0.2/querri/types/project.py +158 -0
  85. querri-1.0.2/querri/types/sharing.py +19 -0
  86. querri-1.0.2/querri/types/usage.py +39 -0
  87. querri-1.0.2/querri/types/user.py +33 -0
  88. querri-1.0.2/skills/README.md +28 -0
  89. querri-1.0.2/skills/querri-cli/SKILL.md +301 -0
  90. querri-1.0.2/skills/querri-cli/references/json-shapes.md +135 -0
  91. querri-1.0.2/skills/querri-cli/references/troubleshooting.md +115 -0
  92. querri-1.0.2/tests/__init__.py +0 -0
  93. querri-1.0.2/tests/conftest.py +19 -0
  94. querri-1.0.2/tests/rls-integration-results.md +81 -0
  95. querri-1.0.2/tests/test_cli.py +530 -0
  96. querri-1.0.2/tests/test_cli_commands.py +2108 -0
  97. querri-1.0.2/tests/test_client.py +166 -0
  98. querri-1.0.2/tests/test_config.py +137 -0
  99. querri-1.0.2/tests/test_convenience.py +169 -0
  100. querri-1.0.2/tests/test_exceptions.py +162 -0
  101. querri-1.0.2/tests/test_integration.py +312 -0
  102. querri-1.0.2/tests/test_new_features.py +257 -0
  103. querri-1.0.2/tests/test_pagination.py +278 -0
  104. querri-1.0.2/tests/test_resources.py +2151 -0
  105. querri-1.0.2/tests/test_rls_integration.py +593 -0
  106. querri-1.0.2/tests/test_security.py +422 -0
  107. querri-1.0.2/tests/test_streaming.py +207 -0
  108. querri-1.0.2/tests/test_streaming_v2.py +528 -0
@@ -0,0 +1,60 @@
1
+ # Contributing to querri
2
+
3
+ Thanks for your interest in contributing! This guide will help you get started.
4
+
5
+ ## Getting started
6
+
7
+ 1. Fork and clone the repository
8
+ 2. Install dependencies:
9
+ ```sh
10
+ pip install -e ".[dev]"
11
+ ```
12
+ 3. Run tests:
13
+ ```sh
14
+ pytest tests/ -v
15
+ ```
16
+ 4. Run linting and type checking:
17
+ ```sh
18
+ ruff check querri/ tests/
19
+ mypy querri/
20
+ ```
21
+
22
+ ## Project structure
23
+
24
+ ```
25
+ querri/
26
+ resources/ API resource classes (Users, Projects, Dashboards, Sources, Data, etc.)
27
+ types/ Pydantic v2 models for API request/response types
28
+ _client.py Querri / AsyncQuerri public entry points
29
+ _base_client.py SyncHTTPClient / AsyncHTTPClient with auth, retries, error mapping
30
+ _user_client.py UserQuerri / AsyncUserQuerri for embed session auth
31
+ _config.py Configuration resolution (constructor args > env vars > defaults)
32
+ _pagination.py Cursor-based auto-paginating iterators
33
+ _streaming.py SSE chat streaming (Vercel AI SDK format)
34
+ _convenience.py High-level helpers (get_session, policy setup)
35
+ _exceptions.py Exception hierarchy (QuerriError, APIError, etc.)
36
+ ```
37
+
38
+ - **Tests**: [pytest](https://docs.pytest.org/) + [pytest-asyncio](https://pytest-asyncio.readthedocs.io/) with [respx](https://lundberg.github.io/respx/) for HTTP mocking
39
+ - **Type checking**: [mypy](https://mypy-lang.org/) in strict mode
40
+ - **Linting**: [ruff](https://docs.astral.sh/ruff/)
41
+
42
+ ## Submitting changes
43
+
44
+ 1. Create a branch from `main`
45
+ 2. Make your changes with clear, descriptive commits
46
+ 3. Ensure CI passes:
47
+ ```sh
48
+ pytest tests/ -v
49
+ ruff check querri/ tests/
50
+ mypy querri/
51
+ ```
52
+ 4. Open a pull request against `main`
53
+
54
+ ## Reporting bugs
55
+
56
+ Please use the [bug report template](https://github.com/querri-ai/querri-python/issues/new?template=bug_report.md) when filing issues.
57
+
58
+ ## Code of conduct
59
+
60
+ Be respectful and constructive. We're building something together — treat others the way you'd like to be treated.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a bug to help us improve querri
4
+ labels: bug
5
+ ---
6
+
7
+ ## Describe the bug
8
+
9
+ A clear and concise description of what the bug is.
10
+
11
+ ## To reproduce
12
+
13
+ Steps to reproduce the behavior:
14
+
15
+ 1. Install `querri` version '...'
16
+ 2. Call the '...' method
17
+ 3. Pass the following options '...'
18
+ 4. See error
19
+
20
+ ## Expected behavior
21
+
22
+ A clear and concise description of what you expected to happen.
23
+
24
+ ## Environment
25
+
26
+ - **querri version**: [e.g. 0.1.0]
27
+ - **Python version**: [e.g. 3.12.0]
28
+ - **Framework**: [e.g. Django 5, Flask 3, FastAPI 0.110, Plain Python]
29
+ - **OS**: [e.g. macOS 14, Ubuntu 24.04, Windows 11]
30
+
31
+ ## Screenshots
32
+
33
+ If applicable, add screenshots to help explain the issue.
34
+
35
+ ## Additional context
36
+
37
+ Add any other context about the problem here.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for querri
4
+ labels: enhancement
5
+ ---
6
+
7
+ ## Is your feature request related to a problem?
8
+
9
+ A clear and concise description of the problem. E.g. "I'm always frustrated when [...]"
10
+
11
+ ## Describe the solution you'd like
12
+
13
+ A clear and concise description of what you want to happen.
14
+
15
+ ## Alternatives considered
16
+
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ ## Additional context
20
+
21
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,19 @@
1
+ ## Description
2
+
3
+ <!-- Summarize your changes and link any related issues (e.g. "Fixes #123"). -->
4
+
5
+ ## Type of change
6
+
7
+ - [ ] Bug fix (non-breaking change that fixes an issue)
8
+ - [ ] New feature (non-breaking change that adds functionality)
9
+ - [ ] Breaking change (fix or feature that would cause existing functionality to change)
10
+ - [ ] Documentation update
11
+
12
+ ## Checklist
13
+
14
+ - [ ] I have performed a self-review of my code
15
+ - [ ] I have added tests that prove my fix or feature works
16
+ - [ ] `pytest tests/ -v` passes locally
17
+ - [ ] `ruff check querri/ tests/` passes locally
18
+ - [ ] `mypy querri/` passes locally
19
+ - [ ] I have updated documentation if needed
@@ -0,0 +1,31 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ If you discover a security vulnerability in `querri`, please report it responsibly.
6
+
7
+ **Do not open a public issue.** Instead, email **security@querri.com** with:
8
+
9
+ - A description of the vulnerability
10
+ - Steps to reproduce it
11
+ - The affected version(s)
12
+
13
+ ## Response timeline
14
+
15
+ - We will acknowledge your report within **48 hours**
16
+ - We will provide an initial assessment and next steps shortly after
17
+
18
+ ## Process
19
+
20
+ 1. Confirm the vulnerability and identify affected versions
21
+ 2. Develop and test a fix
22
+ 3. Release a patched version
23
+ 4. Publicly disclose the issue after the fix is available
24
+
25
+ ## Scope
26
+
27
+ This policy applies to the `querri` PyPI package. For vulnerabilities in the Querri platform itself, please contact security@querri.com directly.
28
+
29
+ ## Feedback
30
+
31
+ Suggestions for improving this process are welcome — feel free to open a pull request.
@@ -0,0 +1,25 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ['v*']
7
+ pull_request:
8
+ branches: [main]
9
+ workflow_call:
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ['3.10', '3.11', '3.12', '3.13']
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - run: pip install -e ".[dev]"
23
+ - run: ruff check querri/ tests/
24
+ - run: mypy querri/
25
+ - run: pytest tests/ -v
@@ -0,0 +1,42 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v[0-9]+.[0-9]+.[0-9]+*'
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ ci:
14
+ uses: ./.github/workflows/ci.yml
15
+
16
+ release:
17
+ needs: ci
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Create GitHub Release
23
+ uses: softprops/action-gh-release@v2
24
+ with:
25
+ tag_name: ${{ github.ref_name }}
26
+ name: ${{ github.ref_name }}
27
+ generate_release_notes: true
28
+ draft: false
29
+ prerelease: ${{ contains(github.ref_name, '-') }}
30
+
31
+ publish:
32
+ needs: ci
33
+ runs-on: ubuntu-latest
34
+ environment: pypi
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: '3.13'
40
+ - run: pip install build
41
+ - run: python -m build
42
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+
23
+ # OS
24
+ .DS_Store
25
+ Thumbs.db
26
+
27
+ # Environment variables
28
+ .env
29
+ .env.*
30
+
31
+ # Testing
32
+ .pytest_cache/
33
+ .coverage
34
+ htmlcov/
querri-1.0.2/CLAUDE.md ADDED
@@ -0,0 +1,93 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Querri Python SDK — a typed Python client for the Querri API, providing both sync (`Querri`) and async (`AsyncQuerri`) clients. Built on httpx + Pydantic v2. Requires Python >=3.9.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Install in development mode (includes test + lint + type-check tools)
13
+ pip install -e ".[dev]"
14
+
15
+ # Run all unit tests
16
+ pytest tests/ -v
17
+
18
+ # Run a single test file
19
+ pytest tests/test_client.py -v
20
+
21
+ # Run a single test by name
22
+ pytest tests/ -k "test_name" -v
23
+
24
+ # Run integration tests (requires QUERRI_API_KEY and QUERRI_ORG_ID env vars)
25
+ pytest tests/test_integration.py -m integration -v
26
+
27
+ # Type checking
28
+ mypy querri/
29
+
30
+ # Linting
31
+ ruff check querri/ tests/
32
+ ```
33
+
34
+ ## Architecture
35
+
36
+ ### Client Layer
37
+ `Querri` / `AsyncQuerri` (`_client.py`) are the public entry points. They hold lazily-initialized resource namespaces (e.g., `client.users`, `client.projects`) and support context-manager usage. They delegate HTTP to `SyncHTTPClient` / `AsyncHTTPClient` (`_base_client.py`), which handle auth headers (Bearer token + X-Tenant-ID), retries with exponential backoff, and error-to-exception mapping.
38
+
39
+ ### User-Scoped Client
40
+ `UserQuerri` / `AsyncUserQuerri` (`_user_client.py`) are created via `client.as_user(session)`. They use `X-Embed-Session` auth against the internal API (`/api` instead of `/api/v1`). Only expose FGA-filtered resources: `projects`, `dashboards`, `sources`, `data`, `chats`.
41
+
42
+ ### Resource Pattern
43
+ Each API resource lives in `querri/resources/` with paired sync/async classes (e.g., `Users` / `AsyncUsers`). Resources receive the HTTP client at init and expose CRUD methods that return Pydantic models from `querri/types/`. Projects has a nested `Chats` sub-resource for chat streaming.
44
+
45
+ ### Key Subsystems
46
+ - **Pagination** (`_pagination.py`): `SyncCursorPage[T]` / `AsyncCursorPage[T]` — cursor-based with offset fallback, auto-paginating via `__iter__`/`__aiter__`.
47
+ - **Streaming** (`_streaming.py`): `ChatStream` / `AsyncChatStream` — SSE parsing (Vercel AI SDK format: `0:` text, `e:` error, `d:` done).
48
+ - **Convenience** (`_convenience.py`): `get_session()` is the flagship method — resolves/creates a user, applies access policies, and creates an embed session in one call. `client.policies.setup()` creates a policy and assigns users in one call.
49
+ - **Config** (`_config.py`): Constructor args > env vars (`QUERRI_API_KEY`, `QUERRI_ORG_ID`, `QUERRI_HOST`, `QUERRI_TIMEOUT`, `QUERRI_MAX_RETRIES`) > defaults.
50
+ - **Exceptions** (`_exceptions.py`): `QuerriError` → `APIError` (with status-specific subclasses like `RateLimitError`), `StreamError`, `ConfigError`.
51
+
52
+ ## Sync/Async Parity
53
+
54
+ Every resource, pagination class, and stream has mirrored sync/async implementations. When modifying code:
55
+ - Always change both sync and async variants simultaneously.
56
+ - Async methods use `await` and `async for` but share identical method signatures and return types.
57
+ - In resource files: sync class comes first, async class second.
58
+ - In `_convenience.py`: shared helpers at top, then sync functions, then async functions.
59
+
60
+ ## Adding a New Resource
61
+
62
+ 1. Create type models in `querri/types/<name>.py` — Pydantic `BaseModel` subclasses with `#:` inline field docs.
63
+ 2. Create `querri/resources/<name>.py` with paired classes:
64
+ - `class Foo:` taking `SyncHTTPClient` at `__init__`
65
+ - `class AsyncFoo:` taking `AsyncHTTPClient` at `__init__`
66
+ - Both must have full Google-style docstrings (Args/Returns) on every method.
67
+ 3. Register lazy properties on both `Querri` and `AsyncQuerri` in `_client.py` (follow the existing deferred-import pattern).
68
+ 4. Export new public types in `querri/__init__.py` if needed.
69
+ 5. Add tests in `tests/test_<name>.py` using respx mocking.
70
+
71
+ ## Testing Patterns
72
+
73
+ - Use `@respx.mock` decorator for HTTP mocking.
74
+ - Create a `_make_config()` helper returning `ClientConfig` with test values (`api_key="qk_test"`, `org_id="org_test"`, `base_url="https://test.querri.com/api/v1"`).
75
+ - Instantiate `SyncHTTPClient(_make_config())` or `AsyncHTTPClient(_make_config())` directly.
76
+ - Mock specific URLs: `respx.get("https://test.querri.com/api/v1/endpoint").mock(return_value=httpx.Response(200, json={...}))`.
77
+ - Use `side_effect=[Response1, Response2]` for multi-page pagination tests.
78
+ - pytest-asyncio runs in `auto` mode — no `@pytest.mark.asyncio` decorator needed on async tests.
79
+ - Integration tests are marked `@pytest.mark.integration` and require `QUERRI_API_KEY` + `QUERRI_ORG_ID` env vars.
80
+
81
+ ## Docstring Conventions
82
+
83
+ - **Style**: Google-style with `Args:`, `Returns:`, `Raises:`, `Example::` sections.
84
+ - **Inline code**: Use double backticks — `` ``value`` ``.
85
+ - **Sync + async**: Both classes get full docstrings with Args/Returns (don't truncate async).
86
+ - **Type model fields**: Use `#:` inline comments on the same line as the field.
87
+ - **Module docstrings**: Every module has a 1-line docstring describing its purpose.
88
+
89
+ ## Code Quality
90
+
91
+ - **mypy**: Strict mode, target Python 3.9
92
+ - **ruff**: Rules E, F, I, N, UP, B, SIM (target Python 3.9)
93
+ - **Tests**: pytest + pytest-asyncio (mode: auto) + respx for HTTP mocking
querri-1.0.2/PKG-INFO ADDED
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: querri
3
+ Version: 1.0.2
4
+ Summary: Python SDK and CLI for the Querri data analysis platform
5
+ Project-URL: Homepage, https://querri.com
6
+ Project-URL: Documentation, https://docs.querri.com/sdk/python
7
+ Project-URL: Repository, https://github.com/querri-ai/querri-python
8
+ Author-email: Querri <support@querri.com>
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: httpx<1.0,>=0.27
21
+ Requires-Dist: pydantic<3.0,>=2.0
22
+ Requires-Dist: typing-extensions>=4.0
23
+ Provides-Extra: cli
24
+ Requires-Dist: pillow<12.0,>=10.0; extra == 'cli'
25
+ Requires-Dist: rich<14.0,>=13.0; extra == 'cli'
26
+ Requires-Dist: typer[all]<1.0,>=0.12; extra == 'cli'
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.0; extra == 'dev'
29
+ Requires-Dist: pillow<12.0,>=10.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: respx>=0.21; extra == 'dev'
33
+ Requires-Dist: rich<14.0,>=13.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.4; extra == 'dev'
35
+ Requires-Dist: typer[all]<1.0,>=0.12; extra == 'dev'
36
+ Provides-Extra: test
37
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
38
+ Requires-Dist: pytest>=8.0; extra == 'test'
39
+ Requires-Dist: respx>=0.21; extra == 'test'
40
+ Provides-Extra: tui
41
+ Requires-Dist: pillow<12.0,>=10.0; extra == 'tui'
42
+ Requires-Dist: rich<14.0,>=13.0; extra == 'tui'
43
+ Requires-Dist: typer[all]<1.0,>=0.12; extra == 'tui'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # Querri — Python SDK and CLI
47
+
48
+ > **CLI:** Interact with the Querri data analysis platform from the terminal or in scripts.
49
+ > **Python SDK:** Embed Querri analytics in your Python web application with one method call.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install querri
55
+ ```
56
+
57
+ ---
58
+
59
+ ## CLI Quick Start
60
+
61
+ The `querri` CLI lets you upload data, create analysis projects, and ask questions — interactively or from scripts.
62
+
63
+ ### Auth
64
+
65
+ ```bash
66
+ querri auth login # browser-based login
67
+ querri whoami # confirm who you're logged in as
68
+ ```
69
+
70
+ For scripted/non-interactive use, set `QUERRI_API_KEY` instead of relying on stored tokens:
71
+
72
+ ```bash
73
+ export QUERRI_API_KEY=qk_your_api_key
74
+ export QUERRI_ORG_ID=org_your_org_id
75
+ ```
76
+
77
+ ### Example workflow: upload a file and analyze it
78
+
79
+ ```bash
80
+ # Upload a file (CSV, Excel, JSON, etc.)
81
+ querri --json file upload path/to/data.csv
82
+
83
+ # Create a project
84
+ querri --json project new "My Analysis"
85
+
86
+ # Add the file to the project (triggers ingestion + agent summary)
87
+ querri --json project add-source <file_id>
88
+
89
+ # Ask a question
90
+ querri --json project chat -m "What are the top 5 products by revenue?"
91
+ ```
92
+
93
+ For scripting, add `--no-interactive` to prevent prompts and `--json` for parseable output. Note that `--json` and other global flags must come **before** the subcommand:
94
+
95
+ ```bash
96
+ querri --json --no-interactive project chat -m "Summarize the data" # correct
97
+ querri project chat -m "Summarize the data" --json # WRONG
98
+ ```
99
+
100
+ ### Self-documenting
101
+
102
+ The CLI covers all Querri resources — projects, files, sources, views, dashboards, sharing, API keys, users, policies, embed sessions, and more.
103
+
104
+ ```bash
105
+ querri --help
106
+ querri <command> --help
107
+ ```
108
+
109
+ See [`skills/querri-cli/SKILL.md`](skills/querri-cli/SKILL.md) for the full command reference.
110
+
111
+ ---
112
+
113
+ ## Python SDK
114
+
115
+ For embedding Querri analytics in a Python web application. Use alongside the [`@querri-inc/embed`](https://www.npmjs.com/package/@querri-inc/embed) frontend component.
116
+
117
+ ### Quick Start
118
+
119
+ ```python
120
+ from querri import Querri
121
+
122
+ client = Querri(api_key="qk_your_api_key", org_id="org_...")
123
+
124
+ session = client.embed.get_session(
125
+ user="customer-42", # external ID from your system
126
+ ttl=3600,
127
+ )
128
+
129
+ print(session["session_token"]) # JWT to pass to the frontend
130
+ ```
131
+
132
+ ### Wire a session endpoint
133
+
134
+ **Flask:**
135
+
136
+ ```python
137
+ from flask import Flask, jsonify, request
138
+ from querri import Querri
139
+
140
+ app = Flask(__name__)
141
+ client = Querri() # reads QUERRI_API_KEY and QUERRI_ORG_ID from env
142
+
143
+ @app.route("/api/querri-session", methods=["POST"])
144
+ def querri_session():
145
+ # Derive user identity from YOUR auth system — never from the request body.
146
+ auth_user = get_authenticated_user()
147
+
148
+ session = client.embed.get_session(
149
+ user={"external_id": auth_user.id, "email": auth_user.email},
150
+ access={
151
+ "sources": ["src_sales_data"],
152
+ "filters": {"tenant_id": auth_user.tenant_id},
153
+ },
154
+ origin=request.headers.get("Origin"),
155
+ ttl=3600,
156
+ )
157
+ return jsonify(session)
158
+ ```
159
+
160
+ Django and FastAPI follow the identical pattern — see **[docs/server-sdk.md](docs/server-sdk.md)** for those examples.
161
+
162
+ ### Add the embed (React)
163
+
164
+ ```tsx
165
+ import { QuerriEmbed } from '@querri-inc/embed/react';
166
+
167
+ <QuerriEmbed
168
+ style={{ width: '100%', height: '600px' }}
169
+ serverUrl="https://app.querri.com"
170
+ auth={{ sessionEndpoint: '/api/querri-session' }}
171
+ />
172
+ ```
173
+
174
+ > **Security:** Always derive user identity and access from server-side auth. Never read `user` or `access` from the request body — a malicious client can impersonate any user or escalate access.
175
+
176
+ ### Configuration
177
+
178
+ The SDK reads configuration from constructor arguments or environment variables:
179
+
180
+ | Parameter | Env Variable | Default | Description |
181
+ |-----------|-------------|---------|-------------|
182
+ | `api_key` | `QUERRI_API_KEY` | *(required)* | Your `qk_` API key |
183
+ | `org_id` | `QUERRI_ORG_ID` | *(required)* | Organization ID |
184
+ | `host` | `QUERRI_HOST` | `https://app.querri.com` | Server host |
185
+ | `timeout` | `QUERRI_TIMEOUT` | `30.0` | Request timeout (seconds) |
186
+ | `max_retries` | `QUERRI_MAX_RETRIES` | `3` | Retry attempts for transient errors |
187
+
188
+ > **Note:** The parameter is `host`, not `base_url`. The SDK appends `/api/v1` automatically.
189
+
190
+ ### `get_session()` — Embed Sessions
191
+
192
+ The flagship convenience method: resolves or creates a user, applies access policies, and generates a session JWT in one call.
193
+
194
+ ```python
195
+ session = client.embed.get_session(
196
+ user={
197
+ "external_id": "customer-42",
198
+ "email": "alice@acme.com",
199
+ "first_name": "Alice",
200
+ },
201
+ access={
202
+ "sources": ["src_sales_data"],
203
+ "filters": {
204
+ "tenant_id": "acme",
205
+ "region": ["us-east", "us-west"], # list values are OR'd
206
+ },
207
+ },
208
+ origin="https://app.acme.com",
209
+ ttl=7200,
210
+ )
211
+
212
+ session["session_token"] # str — JWT for the embed
213
+ session["expires_in"] # int — seconds until expiry
214
+ session["user_id"] # str — Querri user ID
215
+ ```
216
+
217
+ You can also pass pre-created policy IDs directly:
218
+
219
+ ```python
220
+ session = client.embed.get_session(
221
+ user={"external_id": "customer-42"},
222
+ access={"policy_ids": ["pol_abc123"]},
223
+ )
224
+ ```
225
+
226
+ ### User-Scoped Client (`as_user`)
227
+
228
+ ```python
229
+ session = client.embed.get_session(user="customer-42", ttl=900)
230
+
231
+ with client.as_user(session) as user_client:
232
+ for project in user_client.projects.list():
233
+ print(project.name)
234
+ ```
235
+
236
+ See **[docs/server-sdk.md](docs/server-sdk.md#user-scoped-client-as_user)** for details on granting access and available resources.
237
+
238
+ ### All Resources
239
+
240
+ | Resource | Access | Key Methods |
241
+ |----------|--------|-------------|
242
+ | `client.dashboards` | Dashboard management | `list`, `create`, `get`, `update`, `delete`, `refresh` |
243
+ | `client.projects` | Analysis projects | `list`, `create`, `get`, `run`, `run_status`, `list_steps` |
244
+ | `client.projects.chats` | Chats within projects | `create`, `list`, `stream`, `cancel`, `delete` |
245
+ | `client.sources` | Sources, connectors & data | `list`, `create`, `create_data_source`, `query`, `source_data`, `append_rows`, `replace_data`, `ask`, `sync`, `list_connectors` |
246
+ | `client.views` | SQL-defined views | `list`, `create`, `get`, `update`, `delete`, `run`, `preview`, `chat` |
247
+ | `client.files` | File management | `upload`, `list`, `get`, `delete` |
248
+ | `client.users` | User management | `list`, `create`, `get`, `update`, `delete` |
249
+ | `client.keys` | API key management | `create`, `list`, `get`, `delete` |
250
+ | `client.audit` | Audit log | `list` |
251
+ | `client.usage` | Usage metrics | `org_usage`, `user_usage` |
252
+ | `client.sharing` | Sharing & permissions | `share_project`, `share_dashboard`, `share_source` |
253
+
254
+ ### Async Client
255
+
256
+ `AsyncQuerri` mirrors the sync API with `async`/`await`:
257
+
258
+ ```python
259
+ from querri import AsyncQuerri
260
+
261
+ async with AsyncQuerri() as client:
262
+ session = await client.embed.get_session(
263
+ user={"external_id": "cust-42", "email": "a@b.com"},
264
+ access={"sources": ["src_sales"]},
265
+ )
266
+ ```
267
+
268
+ ### Error Handling
269
+
270
+ All errors extend `QuerriError`:
271
+
272
+ ```
273
+ QuerriError
274
+ ├── APIError — HTTP error responses
275
+ │ ├── ValidationError — 400
276
+ │ ├── AuthenticationError — 401
277
+ │ ├── PermissionError — 403
278
+ │ ├── NotFoundError — 404
279
+ │ ├── ConflictError — 409
280
+ │ ├── RateLimitError — 429 (auto-retried)
281
+ │ └── ServerError — 5xx (auto-retried)
282
+ ├── StreamError — SSE stream issues
283
+ └── ConfigError — missing/invalid configuration
284
+ ```
285
+
286
+ The SDK automatically retries **429** (always) and **5xx** (idempotent methods only) with exponential backoff + jitter.
287
+
288
+ ### Full Reference
289
+
290
+ See **[docs/server-sdk.md](docs/server-sdk.md)** for complete method signatures, all framework examples, and the `get_session()` deep dive.
291
+
292
+ ---
293
+
294
+ ## Requirements
295
+
296
+ - Python 3.10+
297
+ - [`httpx`](https://www.python-httpx.org/) >= 0.27
298
+ - [`pydantic`](https://docs.pydantic.dev/) >= 2.0
299
+
300
+ ## Development
301
+
302
+ ```bash
303
+ pip install -e ".[dev]"
304
+ pytest tests/ -v
305
+ pytest tests/test_integration.py -m integration -v # requires API credentials
306
+ ```
307
+
308
+ ## License
309
+
310
+ MIT