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.
Files changed (60) hide show
  1. gulp_sdk-1.0.0/.github/workflows/python-package.yml +79 -0
  2. gulp_sdk-1.0.0/.gitignore +25 -0
  3. gulp_sdk-1.0.0/PKG-INFO +268 -0
  4. gulp_sdk-1.0.0/README.md +246 -0
  5. gulp_sdk-1.0.0/coverage.xml +1828 -0
  6. gulp_sdk-1.0.0/docs/api_reference.md +90 -0
  7. gulp_sdk-1.0.0/docs/examples/basic_usage.py +65 -0
  8. gulp_sdk-1.0.0/docs/examples/collab_notes_links.py +55 -0
  9. gulp_sdk-1.0.0/docs/examples/context_source_management.py +369 -0
  10. gulp_sdk-1.0.0/docs/examples/index.md +15 -0
  11. gulp_sdk-1.0.0/docs/examples/ingest_raw.py +58 -0
  12. gulp_sdk-1.0.0/docs/examples/query_external_elasticsearch.py +45 -0
  13. gulp_sdk-1.0.0/docs/examples/query_gulp_preview.py +43 -0
  14. gulp_sdk-1.0.0/docs/examples/request_status_vs_polling.py +59 -0
  15. gulp_sdk-1.0.0/docs/examples/storage_list_files.py +33 -0
  16. gulp_sdk-1.0.0/docs/index.md +7 -0
  17. gulp_sdk-1.0.0/docs/quickstart.md +79 -0
  18. gulp_sdk-1.0.0/mkdocs.yml +13 -0
  19. gulp_sdk-1.0.0/pyproject.toml +87 -0
  20. gulp_sdk-1.0.0/requirements-docs.txt +2 -0
  21. gulp_sdk-1.0.0/setup.cfg +4 -0
  22. gulp_sdk-1.0.0/src/gulp_sdk/__init__.py +108 -0
  23. gulp_sdk-1.0.0/src/gulp_sdk/api/__init__.py +11 -0
  24. gulp_sdk-1.0.0/src/gulp_sdk/api/acl.py +229 -0
  25. gulp_sdk-1.0.0/src/gulp_sdk/api/auth.py +98 -0
  26. gulp_sdk-1.0.0/src/gulp_sdk/api/collab.py +495 -0
  27. gulp_sdk-1.0.0/src/gulp_sdk/api/db.py +194 -0
  28. gulp_sdk-1.0.0/src/gulp_sdk/api/enrich.py +393 -0
  29. gulp_sdk-1.0.0/src/gulp_sdk/api/ingest.py +689 -0
  30. gulp_sdk-1.0.0/src/gulp_sdk/api/operations.py +554 -0
  31. gulp_sdk-1.0.0/src/gulp_sdk/api/plugins.py +718 -0
  32. gulp_sdk-1.0.0/src/gulp_sdk/api/queries.py +609 -0
  33. gulp_sdk-1.0.0/src/gulp_sdk/api/request_utils.py +237 -0
  34. gulp_sdk-1.0.0/src/gulp_sdk/api/storage.py +173 -0
  35. gulp_sdk-1.0.0/src/gulp_sdk/api/user_groups.py +234 -0
  36. gulp_sdk-1.0.0/src/gulp_sdk/api/users.py +384 -0
  37. gulp_sdk-1.0.0/src/gulp_sdk/client.py +463 -0
  38. gulp_sdk-1.0.0/src/gulp_sdk/exceptions.py +93 -0
  39. gulp_sdk-1.0.0/src/gulp_sdk/models.py +242 -0
  40. gulp_sdk-1.0.0/src/gulp_sdk/pagination.py +160 -0
  41. gulp_sdk-1.0.0/src/gulp_sdk/utils.py +154 -0
  42. gulp_sdk-1.0.0/src/gulp_sdk/websocket.py +504 -0
  43. gulp_sdk-1.0.0/src/gulp_sdk.egg-info/PKG-INFO +268 -0
  44. gulp_sdk-1.0.0/src/gulp_sdk.egg-info/SOURCES.txt +58 -0
  45. gulp_sdk-1.0.0/src/gulp_sdk.egg-info/dependency_links.txt +1 -0
  46. gulp_sdk-1.0.0/src/gulp_sdk.egg-info/requires.txt +16 -0
  47. gulp_sdk-1.0.0/src/gulp_sdk.egg-info/top_level.txt +1 -0
  48. gulp_sdk-1.0.0/tests/__init__.py +3 -0
  49. gulp_sdk-1.0.0/tests/conftest.py +65 -0
  50. gulp_sdk-1.0.0/tests/integration/test_smoke_sdk_roundtrip.py +125 -0
  51. gulp_sdk-1.0.0/tests/unit/test_api_wrappers.py +1229 -0
  52. gulp_sdk-1.0.0/tests/unit/test_basics.py +51 -0
  53. gulp_sdk-1.0.0/tests/unit/test_client_websocket_advanced.py +312 -0
  54. gulp_sdk-1.0.0/tests/unit/test_client_ws_events.py +71 -0
  55. gulp_sdk-1.0.0/tests/unit/test_coverage_finish.py +198 -0
  56. gulp_sdk-1.0.0/tests/unit/test_models.py +150 -0
  57. gulp_sdk-1.0.0/tests/unit/test_pagination.py +62 -0
  58. gulp_sdk-1.0.0/tests/unit/test_request_utils.py +79 -0
  59. gulp_sdk-1.0.0/tests/unit/test_request_wait_ws.py +227 -0
  60. 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
@@ -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
+
@@ -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
+