gulp-sdk 1.0.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.
- gulp_sdk-1.0.0/.github/workflows/python-package.yml +79 -0
- gulp_sdk-1.0.0/.gitignore +25 -0
- gulp_sdk-1.0.0/PKG-INFO +268 -0
- gulp_sdk-1.0.0/README.md +246 -0
- gulp_sdk-1.0.0/coverage.xml +1828 -0
- gulp_sdk-1.0.0/docs/api_reference.md +90 -0
- gulp_sdk-1.0.0/docs/examples/basic_usage.py +65 -0
- gulp_sdk-1.0.0/docs/examples/collab_notes_links.py +55 -0
- gulp_sdk-1.0.0/docs/examples/context_source_management.py +369 -0
- gulp_sdk-1.0.0/docs/examples/index.md +15 -0
- gulp_sdk-1.0.0/docs/examples/ingest_raw.py +58 -0
- gulp_sdk-1.0.0/docs/examples/query_external_elasticsearch.py +45 -0
- gulp_sdk-1.0.0/docs/examples/query_gulp_preview.py +43 -0
- gulp_sdk-1.0.0/docs/examples/request_status_vs_polling.py +59 -0
- gulp_sdk-1.0.0/docs/examples/storage_list_files.py +33 -0
- gulp_sdk-1.0.0/docs/index.md +7 -0
- gulp_sdk-1.0.0/docs/quickstart.md +79 -0
- gulp_sdk-1.0.0/mkdocs.yml +13 -0
- gulp_sdk-1.0.0/pyproject.toml +87 -0
- gulp_sdk-1.0.0/requirements-docs.txt +2 -0
- gulp_sdk-1.0.0/setup.cfg +4 -0
- gulp_sdk-1.0.0/src/gulp_sdk/__init__.py +108 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/__init__.py +11 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/acl.py +229 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/auth.py +98 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/collab.py +495 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/db.py +194 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/enrich.py +393 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/ingest.py +689 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/operations.py +554 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/plugins.py +718 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/queries.py +609 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/request_utils.py +237 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/storage.py +173 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/user_groups.py +234 -0
- gulp_sdk-1.0.0/src/gulp_sdk/api/users.py +384 -0
- gulp_sdk-1.0.0/src/gulp_sdk/client.py +463 -0
- gulp_sdk-1.0.0/src/gulp_sdk/exceptions.py +93 -0
- gulp_sdk-1.0.0/src/gulp_sdk/models.py +242 -0
- gulp_sdk-1.0.0/src/gulp_sdk/pagination.py +160 -0
- gulp_sdk-1.0.0/src/gulp_sdk/utils.py +154 -0
- gulp_sdk-1.0.0/src/gulp_sdk/websocket.py +504 -0
- gulp_sdk-1.0.0/src/gulp_sdk.egg-info/PKG-INFO +268 -0
- gulp_sdk-1.0.0/src/gulp_sdk.egg-info/SOURCES.txt +58 -0
- gulp_sdk-1.0.0/src/gulp_sdk.egg-info/dependency_links.txt +1 -0
- gulp_sdk-1.0.0/src/gulp_sdk.egg-info/requires.txt +16 -0
- gulp_sdk-1.0.0/src/gulp_sdk.egg-info/top_level.txt +1 -0
- gulp_sdk-1.0.0/tests/__init__.py +3 -0
- gulp_sdk-1.0.0/tests/conftest.py +65 -0
- gulp_sdk-1.0.0/tests/integration/test_smoke_sdk_roundtrip.py +125 -0
- gulp_sdk-1.0.0/tests/unit/test_api_wrappers.py +1229 -0
- gulp_sdk-1.0.0/tests/unit/test_basics.py +51 -0
- gulp_sdk-1.0.0/tests/unit/test_client_websocket_advanced.py +312 -0
- gulp_sdk-1.0.0/tests/unit/test_client_ws_events.py +71 -0
- gulp_sdk-1.0.0/tests/unit/test_coverage_finish.py +198 -0
- gulp_sdk-1.0.0/tests/unit/test_models.py +150 -0
- gulp_sdk-1.0.0/tests/unit/test_pagination.py +62 -0
- gulp_sdk-1.0.0/tests/unit/test_request_utils.py +79 -0
- gulp_sdk-1.0.0/tests/unit/test_request_wait_ws.py +227 -0
- gulp_sdk-1.0.0/tests/unit/test_websocket.py +58 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Python package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*', 'test-v*']
|
|
6
|
+
workflow_dispatch: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test, lint and build
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.13'
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
python -m pip install build setuptools_scm[toml] pytest
|
|
25
|
+
|
|
26
|
+
# TODO: write proper muty-python tests and enable this step
|
|
27
|
+
#- name: Run tests
|
|
28
|
+
# run: |
|
|
29
|
+
# pytest -q
|
|
30
|
+
|
|
31
|
+
- name: Build artifacts
|
|
32
|
+
run: |
|
|
33
|
+
# Usa sempre git commit hash (node-and-date) per avere versioni univoche in dev/CI
|
|
34
|
+
export SETUPTOOLS_SCM_LOCAL=node-and-date
|
|
35
|
+
python -m build
|
|
36
|
+
|
|
37
|
+
- name: Check package
|
|
38
|
+
run: |
|
|
39
|
+
python -m pip install twine
|
|
40
|
+
python -m twine check dist/*
|
|
41
|
+
|
|
42
|
+
publish:
|
|
43
|
+
name: Publish to PyPI
|
|
44
|
+
needs: test
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
if: startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/tags/test-v')
|
|
47
|
+
steps:
|
|
48
|
+
- name: Checkout repository
|
|
49
|
+
uses: actions/checkout@v4
|
|
50
|
+
|
|
51
|
+
- name: Set up Python
|
|
52
|
+
uses: actions/setup-python@v5
|
|
53
|
+
with:
|
|
54
|
+
python-version: '3.13'
|
|
55
|
+
|
|
56
|
+
- name: Install packaging dependencies
|
|
57
|
+
run: |
|
|
58
|
+
python -m pip install --upgrade pip
|
|
59
|
+
python -m pip install build setuptools_scm[toml] twine
|
|
60
|
+
|
|
61
|
+
- name: Build
|
|
62
|
+
run: |
|
|
63
|
+
# Usa sempre git commit hash (node-and-date) anche per rilascio/test
|
|
64
|
+
export SETUPTOOLS_SCM_LOCAL=node-and-date
|
|
65
|
+
python -m build
|
|
66
|
+
|
|
67
|
+
- name: Publish to PyPI
|
|
68
|
+
env:
|
|
69
|
+
TWINE_USERNAME: __token__
|
|
70
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN_VALERINO }}
|
|
71
|
+
TWINE_PASSWORD_TEST: ${{ secrets.PYPI_API_TEST_TOKEN_VALERINO }}
|
|
72
|
+
run: |
|
|
73
|
+
if [[ "${GITHUB_REF##*/}" == test-v* ]]; then
|
|
74
|
+
export TWINE_PASSWORD="${TWINE_PASSWORD_TEST}"
|
|
75
|
+
python -m twine upload --verbose--repository-url https://test.pypi.org/legacy/ dist/*
|
|
76
|
+
else
|
|
77
|
+
export TWINE_PASSWORD="${TWINE_PASSWORD}"
|
|
78
|
+
python -m twine upload --verbose dist/*
|
|
79
|
+
fi
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.venv
|
|
7
|
+
pyrightconfig.json
|
|
8
|
+
venv
|
|
9
|
+
|
|
10
|
+
# VSCode
|
|
11
|
+
.vscode/
|
|
12
|
+
|
|
13
|
+
#auto generated docs
|
|
14
|
+
docs/html
|
|
15
|
+
.VSCodeCounter
|
|
16
|
+
|
|
17
|
+
# misc
|
|
18
|
+
.github/agents
|
|
19
|
+
*.egg-info
|
|
20
|
+
build
|
|
21
|
+
.DS_Store
|
|
22
|
+
.specstory
|
|
23
|
+
.coverage
|
|
24
|
+
site
|
|
25
|
+
src/gulp_sdk/_version.py
|
gulp_sdk-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gulp-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Gulp
|
|
5
|
+
Author-email: valerino <valerino@mentat.is>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.24.0
|
|
9
|
+
Requires-Dist: websockets>=12.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0
|
|
11
|
+
Requires-Dist: python-dotenv>=1.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
14
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
16
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy>=1.5; extra == "dev"
|
|
19
|
+
Provides-Extra: docs
|
|
20
|
+
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
21
|
+
Requires-Dist: mkdocstrings[python]>=0.23; extra == "docs"
|
|
22
|
+
|
|
23
|
+
# Gulp Python SDK
|
|
24
|
+
|
|
25
|
+
Async Python SDK for the [Gulp](https://github.com/mentat-is/gulp) document analysis and collaboration platform.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- **Fully Async** — Built on `httpx` and `asyncio` for high-performance, non-blocking I/O
|
|
30
|
+
- **Complete API Coverage** — All major REST endpoints (operations, documents, ingestion, queries, users, collaboration)
|
|
31
|
+
- **WebSocket Support** — Real-time ingestion progress, query results, and collaborative updates
|
|
32
|
+
- **Type-Safe** — Full type hints, Pydantic models, and static typing support
|
|
33
|
+
- **Error Handling** — Comprehensive exception hierarchy with HTTP status codes and response data
|
|
34
|
+
- **Pagination** — Async iterators for large result sets
|
|
35
|
+
- **Retry Logic** — Automatic exponential backoff on transient failures
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
```bash
|
|
39
|
+
pip install gulp-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
If you prefer a dedicated guide, see [docs/quickstart.md](docs/quickstart.md).
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import asyncio
|
|
48
|
+
from gulp_sdk import GulpClient
|
|
49
|
+
|
|
50
|
+
async def main():
|
|
51
|
+
# Connect to Gulp server
|
|
52
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
53
|
+
# Login
|
|
54
|
+
session = await client.auth.login("user@example.com", "password")
|
|
55
|
+
print(f"Logged in: {session.user_id}")
|
|
56
|
+
|
|
57
|
+
# Create operation
|
|
58
|
+
op = await client.operations.create(
|
|
59
|
+
name="My Investigation",
|
|
60
|
+
description="Analyze event logs"
|
|
61
|
+
)
|
|
62
|
+
print(f"Created operation: {op.id}")
|
|
63
|
+
|
|
64
|
+
# Get current user
|
|
65
|
+
user = await client.users.get_current()
|
|
66
|
+
print(f"Current user: {user.display_name}")
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API Reference
|
|
72
|
+
|
|
73
|
+
- [SDK API reference](docs/api_reference.md)
|
|
74
|
+
|
|
75
|
+
## Documentation site
|
|
76
|
+
|
|
77
|
+
This repository includes official docs for local or remote hosting:
|
|
78
|
+
|
|
79
|
+
- Local preview: `mkdocs serve`
|
|
80
|
+
- Build HTML: `mkdocs build`
|
|
81
|
+
|
|
82
|
+
### Run docs locally
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install -r requirements-docs.txt
|
|
86
|
+
mkdocs serve
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Documentation pages
|
|
90
|
+
|
|
91
|
+
- Quick start: `docs/quickstart.md`
|
|
92
|
+
- API reference: `docs/api_reference.md`
|
|
93
|
+
- Examples: `docs/examples/` (scripts)
|
|
94
|
+
|
|
95
|
+
### Authentication
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Login with credentials
|
|
99
|
+
session = await client.auth.login("user", "password")
|
|
100
|
+
|
|
101
|
+
# Token is automatically stored
|
|
102
|
+
# Logout
|
|
103
|
+
await client.auth.logout()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Operations
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Create operation
|
|
110
|
+
op = await client.operations.create("Name", "Description")
|
|
111
|
+
|
|
112
|
+
# Get operation
|
|
113
|
+
op = await client.operations.get(op.id)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Documents
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# Get document
|
|
120
|
+
doc = await client.documents.get(operation_id, document_id)
|
|
121
|
+
|
|
122
|
+
# Query documents (with async iteration)
|
|
123
|
+
async for doc in client.documents.list(operation_id):
|
|
124
|
+
print(doc.content[:100])
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Ingestion
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Ingest file
|
|
131
|
+
job = await client.ingest.file(operation_id, plugin="json", file_path="/path/to/file.json")
|
|
132
|
+
|
|
133
|
+
# Ingest raw data
|
|
134
|
+
job = await client.ingest.raw(operation_id, plugin="json", data={"key": "value"})
|
|
135
|
+
|
|
136
|
+
# Monitor with WebSocket
|
|
137
|
+
async for progress in client.ingest.stream(operation_id, req_id=job.req_id):
|
|
138
|
+
print(f"Progress: {progress.percent}%")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Collaboration
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
# Add note
|
|
145
|
+
note = await client.collab.create_note(operation_id, document_id, "Important finding")
|
|
146
|
+
|
|
147
|
+
# Create link between documents
|
|
148
|
+
link = await client.collab.create_link(doc_id_from, doc_id_to, "related_to")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## WebSocket Real-Time Updates
|
|
152
|
+
|
|
153
|
+
### Auto-Managed Mode
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
async with GulpClient("http://localhost:8080", ws_auto_connect=True) as client:
|
|
157
|
+
# WebSocket automatically connected
|
|
158
|
+
|
|
159
|
+
# Subscribe to document updates
|
|
160
|
+
await client.websocket.subscribe(operation_id)
|
|
161
|
+
|
|
162
|
+
# Receive messages
|
|
163
|
+
async for message in client.websocket:
|
|
164
|
+
print(f"Update: {message.type} — {message.data}")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Manual Mode
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
171
|
+
async with client.websocket() as ws:
|
|
172
|
+
# Authenticate and subscribe
|
|
173
|
+
await ws.subscribe(operation_id, req_id="ingest-123")
|
|
174
|
+
|
|
175
|
+
# Receive real-time updates
|
|
176
|
+
async for message in ws:
|
|
177
|
+
if message.type == "WSDATA_INGEST_RAW_PROGRESS":
|
|
178
|
+
print(f"Ingestion: {message.data.percent}%")
|
|
179
|
+
elif message.type == "WSDATA_QUERY_DONE":
|
|
180
|
+
print(f"Query complete: {len(message.data.documents)} results")
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Request status: websocket vs polling
|
|
184
|
+
|
|
185
|
+
For async operations, realtime websocket monitoring is recommended; polling is a fallback.
|
|
186
|
+
|
|
187
|
+
- WebSocket pattern: use `wait_for_request_stats(client, req_id, timeout, ws_callback=...)`.
|
|
188
|
+
- Polling: `client.plugins.request_get(req_id)` in a loop.
|
|
189
|
+
|
|
190
|
+
See `docs/api_reference.md` for details and examples.
|
|
191
|
+
|
|
192
|
+
For advanced websocket note/QUERY_DONE tracking, consult `tests/integration/test_stress.py`.
|
|
193
|
+
|
|
194
|
+
## Error Handling
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from gulp_sdk import (
|
|
198
|
+
GulpClient,
|
|
199
|
+
AuthenticationError,
|
|
200
|
+
PermissionError,
|
|
201
|
+
NotFoundError,
|
|
202
|
+
ValidationError,
|
|
203
|
+
GulpSDKError,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
207
|
+
try:
|
|
208
|
+
await client.auth.login("user", "pass")
|
|
209
|
+
except AuthenticationError as e:
|
|
210
|
+
print(f"Login failed: {e.message}")
|
|
211
|
+
except GulpSDKError as e:
|
|
212
|
+
print(f"SDK error: {e.message} (status: {e.status_code})")
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
Environment variables are used by tests and example scripts (fixtures) to parameterize connection settings. The SDK core methods in `src/gulp_sdk` accept explicit arguments and do not read env vars directly.
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
GULP_BASE_URL=http://localhost:8080 # Server URL (default: localhost:8080)
|
|
221
|
+
GULP_TEST_USER=admin # Default test user for integration tests
|
|
222
|
+
GULP_TEST_PASSWORD=admin # Default test password for integration tests
|
|
223
|
+
GULP_TEST_TOKEN= # Optional token for test auth
|
|
224
|
+
GULP_REQUEST_TIMEOUT=30 # HTTP timeout in seconds (default: 30)
|
|
225
|
+
GULP_WS_TIMEOUT=300 # WebSocket timeout (default: 300)
|
|
226
|
+
GULP_LOG_LEVEL=INFO # Logging level (default: INFO)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Or programmatically:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
client = GulpClient(
|
|
233
|
+
base_url="http://localhost:8080",
|
|
234
|
+
timeout=30.0,
|
|
235
|
+
ws_auto_connect=True,
|
|
236
|
+
)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Examples
|
|
240
|
+
|
|
241
|
+
See `docs/examples/` for complete working examples:
|
|
242
|
+
|
|
243
|
+
- [basic_usage.py](docs/examples/basic_usage.py) — Login, create operation, ingest, query
|
|
244
|
+
- [websocket_monitoring.py](docs/examples/websocket_monitoring.py) — Real-time ingestion progress
|
|
245
|
+
- [ingest_raw.py](docs/examples/ingest_raw.py) — Raw JSON ingestion + query_raw preview
|
|
246
|
+
- [query_gulp_preview.py](docs/examples/query_gulp_preview.py) — query_gulp preview with simple filter
|
|
247
|
+
- [collab_notes_links.py](docs/examples/collab_notes_links.py) — Collaboration notes and links workflow
|
|
248
|
+
- [storage_list_files.py](docs/examples/storage_list_files.py) — List and delete storage files workflow
|
|
249
|
+
- [query_external_elasticsearch.py](docs/examples/query_external_elasticsearch.py) — External query with query_elasticsearch plugin
|
|
250
|
+
- [context_source_management.py](docs/examples/context_source_management.py) — Create and manage contexts and sources, complete incident setup
|
|
251
|
+
|
|
252
|
+
For additional workflows, consult the integration tests in `tests/integration/` and the main `gulp` docs for plugin-specific ingestion and query patterns.
|
|
253
|
+
|
|
254
|
+
## Testing
|
|
255
|
+
|
|
256
|
+
Run tests:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Unit tests (no dependencies)
|
|
260
|
+
pytest -v -s -x tests/unit
|
|
261
|
+
|
|
262
|
+
# minimal integration tests, requires live Gulp server on localhost:8080
|
|
263
|
+
pytest -v -s -x tests/integration
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
> to run full integration test suite, look at gulp [gulp testing documentation](https://github.com/mentat-is/gulp/blob/develop/docs/testing.md)
|
|
267
|
+
|
|
268
|
+
|
gulp_sdk-1.0.0/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Gulp Python SDK
|
|
2
|
+
|
|
3
|
+
Async Python SDK for the [Gulp](https://github.com/mentat-is/gulp) document analysis and collaboration platform.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Fully Async** — Built on `httpx` and `asyncio` for high-performance, non-blocking I/O
|
|
8
|
+
- **Complete API Coverage** — All major REST endpoints (operations, documents, ingestion, queries, users, collaboration)
|
|
9
|
+
- **WebSocket Support** — Real-time ingestion progress, query results, and collaborative updates
|
|
10
|
+
- **Type-Safe** — Full type hints, Pydantic models, and static typing support
|
|
11
|
+
- **Error Handling** — Comprehensive exception hierarchy with HTTP status codes and response data
|
|
12
|
+
- **Pagination** — Async iterators for large result sets
|
|
13
|
+
- **Retry Logic** — Automatic exponential backoff on transient failures
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
```bash
|
|
17
|
+
pip install gulp-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
If you prefer a dedicated guide, see [docs/quickstart.md](docs/quickstart.md).
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import asyncio
|
|
26
|
+
from gulp_sdk import GulpClient
|
|
27
|
+
|
|
28
|
+
async def main():
|
|
29
|
+
# Connect to Gulp server
|
|
30
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
31
|
+
# Login
|
|
32
|
+
session = await client.auth.login("user@example.com", "password")
|
|
33
|
+
print(f"Logged in: {session.user_id}")
|
|
34
|
+
|
|
35
|
+
# Create operation
|
|
36
|
+
op = await client.operations.create(
|
|
37
|
+
name="My Investigation",
|
|
38
|
+
description="Analyze event logs"
|
|
39
|
+
)
|
|
40
|
+
print(f"Created operation: {op.id}")
|
|
41
|
+
|
|
42
|
+
# Get current user
|
|
43
|
+
user = await client.users.get_current()
|
|
44
|
+
print(f"Current user: {user.display_name}")
|
|
45
|
+
|
|
46
|
+
asyncio.run(main())
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
- [SDK API reference](docs/api_reference.md)
|
|
52
|
+
|
|
53
|
+
## Documentation site
|
|
54
|
+
|
|
55
|
+
This repository includes official docs for local or remote hosting:
|
|
56
|
+
|
|
57
|
+
- Local preview: `mkdocs serve`
|
|
58
|
+
- Build HTML: `mkdocs build`
|
|
59
|
+
|
|
60
|
+
### Run docs locally
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install -r requirements-docs.txt
|
|
64
|
+
mkdocs serve
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Documentation pages
|
|
68
|
+
|
|
69
|
+
- Quick start: `docs/quickstart.md`
|
|
70
|
+
- API reference: `docs/api_reference.md`
|
|
71
|
+
- Examples: `docs/examples/` (scripts)
|
|
72
|
+
|
|
73
|
+
### Authentication
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# Login with credentials
|
|
77
|
+
session = await client.auth.login("user", "password")
|
|
78
|
+
|
|
79
|
+
# Token is automatically stored
|
|
80
|
+
# Logout
|
|
81
|
+
await client.auth.logout()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Operations
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Create operation
|
|
88
|
+
op = await client.operations.create("Name", "Description")
|
|
89
|
+
|
|
90
|
+
# Get operation
|
|
91
|
+
op = await client.operations.get(op.id)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Documents
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# Get document
|
|
98
|
+
doc = await client.documents.get(operation_id, document_id)
|
|
99
|
+
|
|
100
|
+
# Query documents (with async iteration)
|
|
101
|
+
async for doc in client.documents.list(operation_id):
|
|
102
|
+
print(doc.content[:100])
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Ingestion
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Ingest file
|
|
109
|
+
job = await client.ingest.file(operation_id, plugin="json", file_path="/path/to/file.json")
|
|
110
|
+
|
|
111
|
+
# Ingest raw data
|
|
112
|
+
job = await client.ingest.raw(operation_id, plugin="json", data={"key": "value"})
|
|
113
|
+
|
|
114
|
+
# Monitor with WebSocket
|
|
115
|
+
async for progress in client.ingest.stream(operation_id, req_id=job.req_id):
|
|
116
|
+
print(f"Progress: {progress.percent}%")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Collaboration
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Add note
|
|
123
|
+
note = await client.collab.create_note(operation_id, document_id, "Important finding")
|
|
124
|
+
|
|
125
|
+
# Create link between documents
|
|
126
|
+
link = await client.collab.create_link(doc_id_from, doc_id_to, "related_to")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## WebSocket Real-Time Updates
|
|
130
|
+
|
|
131
|
+
### Auto-Managed Mode
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
async with GulpClient("http://localhost:8080", ws_auto_connect=True) as client:
|
|
135
|
+
# WebSocket automatically connected
|
|
136
|
+
|
|
137
|
+
# Subscribe to document updates
|
|
138
|
+
await client.websocket.subscribe(operation_id)
|
|
139
|
+
|
|
140
|
+
# Receive messages
|
|
141
|
+
async for message in client.websocket:
|
|
142
|
+
print(f"Update: {message.type} — {message.data}")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Manual Mode
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
149
|
+
async with client.websocket() as ws:
|
|
150
|
+
# Authenticate and subscribe
|
|
151
|
+
await ws.subscribe(operation_id, req_id="ingest-123")
|
|
152
|
+
|
|
153
|
+
# Receive real-time updates
|
|
154
|
+
async for message in ws:
|
|
155
|
+
if message.type == "WSDATA_INGEST_RAW_PROGRESS":
|
|
156
|
+
print(f"Ingestion: {message.data.percent}%")
|
|
157
|
+
elif message.type == "WSDATA_QUERY_DONE":
|
|
158
|
+
print(f"Query complete: {len(message.data.documents)} results")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Request status: websocket vs polling
|
|
162
|
+
|
|
163
|
+
For async operations, realtime websocket monitoring is recommended; polling is a fallback.
|
|
164
|
+
|
|
165
|
+
- WebSocket pattern: use `wait_for_request_stats(client, req_id, timeout, ws_callback=...)`.
|
|
166
|
+
- Polling: `client.plugins.request_get(req_id)` in a loop.
|
|
167
|
+
|
|
168
|
+
See `docs/api_reference.md` for details and examples.
|
|
169
|
+
|
|
170
|
+
For advanced websocket note/QUERY_DONE tracking, consult `tests/integration/test_stress.py`.
|
|
171
|
+
|
|
172
|
+
## Error Handling
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from gulp_sdk import (
|
|
176
|
+
GulpClient,
|
|
177
|
+
AuthenticationError,
|
|
178
|
+
PermissionError,
|
|
179
|
+
NotFoundError,
|
|
180
|
+
ValidationError,
|
|
181
|
+
GulpSDKError,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
async with GulpClient("http://localhost:8080") as client:
|
|
185
|
+
try:
|
|
186
|
+
await client.auth.login("user", "pass")
|
|
187
|
+
except AuthenticationError as e:
|
|
188
|
+
print(f"Login failed: {e.message}")
|
|
189
|
+
except GulpSDKError as e:
|
|
190
|
+
print(f"SDK error: {e.message} (status: {e.status_code})")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Configuration
|
|
194
|
+
|
|
195
|
+
Environment variables are used by tests and example scripts (fixtures) to parameterize connection settings. The SDK core methods in `src/gulp_sdk` accept explicit arguments and do not read env vars directly.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
GULP_BASE_URL=http://localhost:8080 # Server URL (default: localhost:8080)
|
|
199
|
+
GULP_TEST_USER=admin # Default test user for integration tests
|
|
200
|
+
GULP_TEST_PASSWORD=admin # Default test password for integration tests
|
|
201
|
+
GULP_TEST_TOKEN= # Optional token for test auth
|
|
202
|
+
GULP_REQUEST_TIMEOUT=30 # HTTP timeout in seconds (default: 30)
|
|
203
|
+
GULP_WS_TIMEOUT=300 # WebSocket timeout (default: 300)
|
|
204
|
+
GULP_LOG_LEVEL=INFO # Logging level (default: INFO)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Or programmatically:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
client = GulpClient(
|
|
211
|
+
base_url="http://localhost:8080",
|
|
212
|
+
timeout=30.0,
|
|
213
|
+
ws_auto_connect=True,
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
See `docs/examples/` for complete working examples:
|
|
220
|
+
|
|
221
|
+
- [basic_usage.py](docs/examples/basic_usage.py) — Login, create operation, ingest, query
|
|
222
|
+
- [websocket_monitoring.py](docs/examples/websocket_monitoring.py) — Real-time ingestion progress
|
|
223
|
+
- [ingest_raw.py](docs/examples/ingest_raw.py) — Raw JSON ingestion + query_raw preview
|
|
224
|
+
- [query_gulp_preview.py](docs/examples/query_gulp_preview.py) — query_gulp preview with simple filter
|
|
225
|
+
- [collab_notes_links.py](docs/examples/collab_notes_links.py) — Collaboration notes and links workflow
|
|
226
|
+
- [storage_list_files.py](docs/examples/storage_list_files.py) — List and delete storage files workflow
|
|
227
|
+
- [query_external_elasticsearch.py](docs/examples/query_external_elasticsearch.py) — External query with query_elasticsearch plugin
|
|
228
|
+
- [context_source_management.py](docs/examples/context_source_management.py) — Create and manage contexts and sources, complete incident setup
|
|
229
|
+
|
|
230
|
+
For additional workflows, consult the integration tests in `tests/integration/` and the main `gulp` docs for plugin-specific ingestion and query patterns.
|
|
231
|
+
|
|
232
|
+
## Testing
|
|
233
|
+
|
|
234
|
+
Run tests:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Unit tests (no dependencies)
|
|
238
|
+
pytest -v -s -x tests/unit
|
|
239
|
+
|
|
240
|
+
# minimal integration tests, requires live Gulp server on localhost:8080
|
|
241
|
+
pytest -v -s -x tests/integration
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
> to run full integration test suite, look at gulp [gulp testing documentation](https://github.com/mentat-is/gulp/blob/develop/docs/testing.md)
|
|
245
|
+
|
|
246
|
+
|