snowloader 0.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.
Files changed (59) hide show
  1. snowloader-0.1.0/.github/workflows/ci.yml +80 -0
  2. snowloader-0.1.0/.github/workflows/publish.yml +52 -0
  3. snowloader-0.1.0/.gitignore +28 -0
  4. snowloader-0.1.0/.readthedocs.yaml +17 -0
  5. snowloader-0.1.0/CHANGELOG.md +44 -0
  6. snowloader-0.1.0/LICENSE +21 -0
  7. snowloader-0.1.0/PKG-INFO +248 -0
  8. snowloader-0.1.0/README.md +198 -0
  9. snowloader-0.1.0/docs/adapters.rst +124 -0
  10. snowloader-0.1.0/docs/advanced.rst +161 -0
  11. snowloader-0.1.0/docs/api.rst +63 -0
  12. snowloader-0.1.0/docs/authentication.rst +133 -0
  13. snowloader-0.1.0/docs/changelog.rst +50 -0
  14. snowloader-0.1.0/docs/conf.py +91 -0
  15. snowloader-0.1.0/docs/configuration.rst +159 -0
  16. snowloader-0.1.0/docs/getting-started.rst +147 -0
  17. snowloader-0.1.0/docs/index.rst +55 -0
  18. snowloader-0.1.0/docs/loaders.rst +139 -0
  19. snowloader-0.1.0/docs/roadmap.rst +85 -0
  20. snowloader-0.1.0/docs/servicenow_api.md +92 -0
  21. snowloader-0.1.0/examples/01_basic_incidents.py +46 -0
  22. snowloader-0.1.0/examples/02_langchain_rag.py +56 -0
  23. snowloader-0.1.0/examples/03_llamaindex_rag.py +50 -0
  24. snowloader-0.1.0/examples/04_delta_sync.py +70 -0
  25. snowloader-0.1.0/examples/05_cmdb_graph.py +58 -0
  26. snowloader-0.1.0/pyproject.toml +100 -0
  27. snowloader-0.1.0/src/snowloader/__init__.py +36 -0
  28. snowloader-0.1.0/src/snowloader/adapters/__init__.py +16 -0
  29. snowloader-0.1.0/src/snowloader/adapters/langchain.py +110 -0
  30. snowloader-0.1.0/src/snowloader/adapters/llamaindex.py +127 -0
  31. snowloader-0.1.0/src/snowloader/connection.py +670 -0
  32. snowloader-0.1.0/src/snowloader/loaders/__init__.py +25 -0
  33. snowloader-0.1.0/src/snowloader/loaders/_field_utils.py +88 -0
  34. snowloader-0.1.0/src/snowloader/loaders/catalog.py +94 -0
  35. snowloader-0.1.0/src/snowloader/loaders/changes.py +132 -0
  36. snowloader-0.1.0/src/snowloader/loaders/cmdb.py +268 -0
  37. snowloader-0.1.0/src/snowloader/loaders/incidents.py +158 -0
  38. snowloader-0.1.0/src/snowloader/loaders/knowledge_base.py +103 -0
  39. snowloader-0.1.0/src/snowloader/loaders/problems.py +137 -0
  40. snowloader-0.1.0/src/snowloader/models.py +260 -0
  41. snowloader-0.1.0/src/snowloader/py.typed +0 -0
  42. snowloader-0.1.0/src/snowloader/utils/__init__.py +14 -0
  43. snowloader-0.1.0/src/snowloader/utils/html_cleaner.py +73 -0
  44. snowloader-0.1.0/tests/__init__.py +1 -0
  45. snowloader-0.1.0/tests/integration/__init__.py +5 -0
  46. snowloader-0.1.0/tests/integration/test_live.py +579 -0
  47. snowloader-0.1.0/tests/unit/__init__.py +1 -0
  48. snowloader-0.1.0/tests/unit/test_catalog.py +101 -0
  49. snowloader-0.1.0/tests/unit/test_changes.py +142 -0
  50. snowloader-0.1.0/tests/unit/test_cmdb.py +316 -0
  51. snowloader-0.1.0/tests/unit/test_connection.py +314 -0
  52. snowloader-0.1.0/tests/unit/test_html_cleaner.py +56 -0
  53. snowloader-0.1.0/tests/unit/test_incidents.py +235 -0
  54. snowloader-0.1.0/tests/unit/test_kb.py +199 -0
  55. snowloader-0.1.0/tests/unit/test_langchain_adapter.py +175 -0
  56. snowloader-0.1.0/tests/unit/test_llamaindex_adapter.py +171 -0
  57. snowloader-0.1.0/tests/unit/test_models.py +232 -0
  58. snowloader-0.1.0/tests/unit/test_problems.py +129 -0
  59. snowloader-0.1.0/tests/unit/test_smoke_e2e.py +994 -0
@@ -0,0 +1,80 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ name: Lint & Type Check
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.13"
23
+
24
+ - name: Install dependencies
25
+ run: pip install -e ".[all,dev]"
26
+
27
+ - name: Ruff check
28
+ run: ruff check src/ tests/
29
+
30
+ - name: Ruff format check
31
+ run: ruff format --check src/ tests/
32
+
33
+ - name: Mypy
34
+ run: mypy src/snowloader/
35
+
36
+ test:
37
+ name: Test (Python ${{ matrix.python-version }})
38
+ runs-on: ubuntu-latest
39
+ strategy:
40
+ fail-fast: false
41
+ matrix:
42
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
43
+
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+
47
+ - name: Set up Python ${{ matrix.python-version }}
48
+ uses: actions/setup-python@v5
49
+ with:
50
+ python-version: ${{ matrix.python-version }}
51
+
52
+ - name: Install dependencies
53
+ run: pip install -e ".[all,dev]"
54
+
55
+ - name: Run unit tests
56
+ run: pytest tests/unit/ -x --tb=short -q --cov=snowloader --cov-report=term-missing
57
+
58
+ - name: Upload coverage
59
+ if: matrix.python-version == '3.13'
60
+ uses: actions/upload-artifact@v4
61
+ with:
62
+ name: coverage-report
63
+ path: .coverage
64
+
65
+ docs:
66
+ name: Build Docs
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+
71
+ - name: Set up Python
72
+ uses: actions/setup-python@v5
73
+ with:
74
+ python-version: "3.13"
75
+
76
+ - name: Install dependencies
77
+ run: pip install -e ".[docs]"
78
+
79
+ - name: Build Sphinx docs
80
+ run: sphinx-build -b html docs/ docs/_build/html
@@ -0,0 +1,52 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build Distribution
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.13"
22
+
23
+ - name: Install build tools
24
+ run: pip install build
25
+
26
+ - name: Build package
27
+ run: python -m build
28
+
29
+ - name: Upload distribution artifacts
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: dist
33
+ path: dist/
34
+
35
+ publish:
36
+ name: Publish to PyPI
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ environment:
40
+ name: pypi
41
+ url: https://pypi.org/p/snowloader
42
+ permissions:
43
+ id-token: write
44
+ steps:
45
+ - name: Download distribution artifacts
46
+ uses: actions/download-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+
51
+ - name: Publish to PyPI
52
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,28 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+ .env
15
+ .venv/
16
+ venv/
17
+ *.log
18
+ .DS_Store
19
+
20
+ # Documentation build output
21
+ docs/_build/
22
+
23
+ # Project management files (internal use only)
24
+ CLAUDE.md
25
+ .claude/
26
+ .status
27
+ GETTING_STARTED.md
28
+ docs/tasks/
@@ -0,0 +1,17 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-22.04
5
+ tools:
6
+ python: "3.13"
7
+
8
+ sphinx:
9
+ configuration: docs/conf.py
10
+ fail_on_warning: false
11
+
12
+ python:
13
+ install:
14
+ - method: pip
15
+ path: .
16
+ extra_requirements:
17
+ - docs
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to snowloader are documented here. This project follows [Semantic Versioning](https://semver.org/).
4
+
5
+ ## [0.1.0] - 2026-03-25
6
+
7
+ ### Added
8
+
9
+ **Loaders:**
10
+ - `IncidentLoader` — IT incidents with structured text and journal support
11
+ - `KnowledgeBaseLoader` — KB articles with built-in HTML cleaning
12
+ - `CMDBLoader` — Configuration Items with concurrent relationship traversal
13
+ - `ChangeLoader` — Change requests with implementation window details
14
+ - `ProblemLoader` — Problems with root cause and known error handling
15
+ - `CatalogLoader` — Service catalog items
16
+
17
+ **Framework Adapters:**
18
+ - LangChain adapter (6 classes implementing `BaseLoader`)
19
+ - LlamaIndex adapter (6 classes implementing `BaseReader`)
20
+
21
+ **Connection:**
22
+ - 4 authentication modes: Basic, OAuth Password Grant, OAuth Client Credentials, Bearer Token
23
+ - Automatic pagination with stable ordering (`ORDERBYsys_created_on`)
24
+ - Retry logic with exponential backoff for 429/502/503/504
25
+ - Rate limiting (configurable `request_delay`)
26
+ - Thread-safe HTTP via request lock
27
+ - Proxy and custom CA certificate support
28
+ - Context manager for session lifecycle
29
+ - Configurable timeout, page size, display value mode
30
+
31
+ **Core Features:**
32
+ - Delta sync via `load_since(datetime)`
33
+ - Memory-efficient streaming via generator-based `lazy_load()`
34
+ - Built-in HTML cleaner (zero external dependencies)
35
+ - Journal entry support (work notes and comments)
36
+ - `SnowDocument` as framework-agnostic intermediate format
37
+ - PEP 561 `py.typed` marker for type checker support
38
+
39
+ **Testing:**
40
+ - 124 unit tests with mocked HTTP
41
+ - 33 live integration tests against a real ServiceNow instance
42
+ - Full quality gate: ruff, mypy --strict, pytest
43
+
44
+ [0.1.0]: https://github.com/ronidas39/snowloader/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roni Das
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: snowloader
3
+ Version: 0.1.0
4
+ Summary: Comprehensive ServiceNow data loader for AI/LLM pipelines — Incidents, CMDB, KB, Changes, Catalog & more. Works with LangChain & LlamaIndex.
5
+ Project-URL: Homepage, https://github.com/ronidas39/snowloader
6
+ Project-URL: Documentation, https://snowloader.readthedocs.io
7
+ Project-URL: Repository, https://github.com/ronidas39/snowloader
8
+ Project-URL: Issues, https://github.com/ronidas39/snowloader/issues
9
+ Project-URL: Changelog, https://github.com/ronidas39/snowloader/blob/main/CHANGELOG.md
10
+ Author-email: Roni Das <roni@totaltechnologyzone.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: agentic-ai,ai,cmdb,data-loader,document-loader,incidents,itsm,knowledge-base,langchain,llamaindex,llm,rag,servicenow,vector-database
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: pydantic>=2.0.0
27
+ Requires-Dist: requests>=2.28.0
28
+ Provides-Extra: all
29
+ Requires-Dist: langchain-core>=0.2.0; extra == 'all'
30
+ Requires-Dist: llama-index-core>=0.11.0; extra == 'all'
31
+ Provides-Extra: dev
32
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
33
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
34
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
35
+ Requires-Dist: responses>=0.23.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
37
+ Requires-Dist: types-requests>=2.28.0; extra == 'dev'
38
+ Provides-Extra: docs
39
+ Requires-Dist: furo>=2024.1.0; extra == 'docs'
40
+ Requires-Dist: myst-parser>=3.0.0; extra == 'docs'
41
+ Requires-Dist: sphinx-autodoc-typehints>=2.0.0; extra == 'docs'
42
+ Requires-Dist: sphinx-copybutton>=0.5.0; extra == 'docs'
43
+ Requires-Dist: sphinx-inline-tabs>=2023.4.0; extra == 'docs'
44
+ Requires-Dist: sphinx>=7.0.0; extra == 'docs'
45
+ Provides-Extra: langchain
46
+ Requires-Dist: langchain-core>=0.2.0; extra == 'langchain'
47
+ Provides-Extra: llamaindex
48
+ Requires-Dist: llama-index-core>=0.11.0; extra == 'llamaindex'
49
+ Description-Content-Type: text/markdown
50
+
51
+ # snowloader
52
+
53
+ [![PyPI version](https://img.shields.io/pypi/v/snowloader.svg)](https://pypi.org/project/snowloader/)
54
+ [![Python versions](https://img.shields.io/pypi/pyversions/snowloader.svg)](https://pypi.org/project/snowloader/)
55
+ [![CI](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml/badge.svg)](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml)
56
+ [![Documentation](https://readthedocs.org/projects/snowloader/badge/?version=latest)](https://snowloader.readthedocs.io)
57
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
58
+ [![Typed](https://img.shields.io/badge/typing-typed-blue.svg)](https://peps.python.org/pep-0561/)
59
+
60
+ > Comprehensive ServiceNow data loader for AI/LLM pipelines — Incidents, CMDB, KB, Changes, Problems, Catalog & more.
61
+
62
+ **Works with LangChain & LlamaIndex out of the box. Python 3.10–3.13.**
63
+
64
+ **[Documentation](https://snowloader.readthedocs.io)** | **[PyPI](https://pypi.org/project/snowloader/)** | **[GitHub](https://github.com/ronidas39/snowloader)**
65
+
66
+ ---
67
+
68
+ ## Why snowloader?
69
+
70
+ Building RAG or agentic AI on top of ServiceNow data? You need a reliable way to pull structured ITSM records into your vector store. Existing tools either cover a single table, ignore relationships, or lock you into one framework.
71
+
72
+ snowloader gives you:
73
+
74
+ - **6 loaders** covering the core ServiceNow tables (Incidents, Knowledge Base, CMDB, Changes, Problems, Service Catalog)
75
+ - **CMDB relationship traversal** — concurrent graph walking with dependency mapping
76
+ - **Delta sync** — only fetch records updated since your last sync
77
+ - **4 auth modes** — Basic, OAuth Password, OAuth Client Credentials, Bearer Token
78
+ - **Production-grade** — retry with backoff, rate limiting, thread safety, proxy support
79
+ - **Framework-agnostic core** with thin adapters for LangChain and LlamaIndex
80
+ - **Memory-efficient streaming** — generator-based pagination, never holds the full table in memory
81
+ - **Built-in HTML cleaning** — strips KB article HTML without extra dependencies
82
+ - **Fully typed** — PEP 561 compliant, mypy --strict clean
83
+
84
+ ## Installation
85
+
86
+ ```bash
87
+ # pip
88
+ pip install snowloader # Core only
89
+ pip install snowloader[langchain] # + LangChain adapter
90
+ pip install snowloader[llamaindex] # + LlamaIndex adapter
91
+ pip install snowloader[all] # Everything
92
+
93
+ # uv
94
+ uv add snowloader
95
+ uv add snowloader[all]
96
+ ```
97
+
98
+ **Requirements:** Python 3.10+ and a ServiceNow instance with REST API access.
99
+
100
+ ## Quick Start
101
+
102
+ ```python
103
+ from snowloader import SnowConnection, IncidentLoader
104
+
105
+ conn = SnowConnection(
106
+ instance_url="https://mycompany.service-now.com",
107
+ username="admin",
108
+ password="password",
109
+ )
110
+
111
+ loader = IncidentLoader(connection=conn, query="active=true^priority<=2")
112
+ for doc in loader.lazy_load():
113
+ print(doc.page_content[:200])
114
+ ```
115
+
116
+ ## All 6 Loaders
117
+
118
+ Every loader shares the same interface: `load()` returns a list, `lazy_load()` yields one document at a time, `load_since(datetime)` fetches only updated records.
119
+
120
+ ```python
121
+ from snowloader import (
122
+ IncidentLoader, # IT incidents
123
+ KnowledgeBaseLoader, # KB articles (HTML auto-cleaned)
124
+ CMDBLoader, # Configuration items + relationships
125
+ ChangeLoader, # Change requests
126
+ ProblemLoader, # Problem records
127
+ CatalogLoader, # Service catalog items
128
+ )
129
+ ```
130
+
131
+ ## LangChain Adapter
132
+
133
+ ```python
134
+ from snowloader import SnowConnection
135
+ from snowloader.adapters.langchain import ServiceNowIncidentLoader
136
+
137
+ conn = SnowConnection(instance_url="...", username="...", password="...")
138
+ loader = ServiceNowIncidentLoader(connection=conn, query="active=true")
139
+ docs = loader.load() # list[langchain_core.documents.Document]
140
+
141
+ # Use with any vector store
142
+ from langchain_community.vectorstores import FAISS
143
+ from langchain_openai import OpenAIEmbeddings
144
+
145
+ vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
146
+ ```
147
+
148
+ ## LlamaIndex Adapter
149
+
150
+ ```python
151
+ from snowloader.adapters.llamaindex import ServiceNowIncidentReader
152
+
153
+ reader = ServiceNowIncidentReader(connection=conn, query="active=true")
154
+ docs = reader.load_data() # list[llama_index.core.schema.Document]
155
+
156
+ from llama_index.core import VectorStoreIndex
157
+ index = VectorStoreIndex.from_documents(docs)
158
+ ```
159
+
160
+ ## Delta Sync
161
+
162
+ ```python
163
+ from datetime import datetime, timezone
164
+
165
+ loader = IncidentLoader(connection=conn)
166
+ docs = loader.load() # First run: everything
167
+ last_sync = datetime.now(timezone.utc)
168
+
169
+ updated = loader.load_since(last_sync) # Next runs: only changes
170
+ ```
171
+
172
+ ## CMDB Relationship Traversal
173
+
174
+ ```python
175
+ loader = CMDBLoader(
176
+ connection=conn,
177
+ ci_class="cmdb_ci_server",
178
+ include_relationships=True,
179
+ )
180
+
181
+ for doc in loader.lazy_load():
182
+ # -> db-prod-01 (Depends on::Used by)
183
+ # <- load-balancer-01 (Depends on::Used by)
184
+ print(doc.page_content)
185
+ ```
186
+
187
+ ## Authentication
188
+
189
+ ```python
190
+ # Basic Auth (development)
191
+ conn = SnowConnection(instance_url="...", username="admin", password="pass")
192
+
193
+ # OAuth Client Credentials (recommended for production)
194
+ conn = SnowConnection(instance_url="...", client_id="...", client_secret="...")
195
+
196
+ # OAuth Password Grant
197
+ conn = SnowConnection(instance_url="...", client_id="...", client_secret="...",
198
+ username="...", password="...")
199
+
200
+ # Bearer Token (pre-obtained)
201
+ conn = SnowConnection(instance_url="...", token="eyJhbG...")
202
+ ```
203
+
204
+ ## Configuration
205
+
206
+ | Parameter | Default | Description |
207
+ |-----------|---------|-------------|
208
+ | `page_size` | `100` | Records per API call (1–10,000) |
209
+ | `timeout` | `60` | HTTP timeout in seconds |
210
+ | `max_retries` | `3` | Retry attempts for 429/502/503/504 |
211
+ | `retry_backoff` | `1.0` | Base delay between retries (doubles each attempt) |
212
+ | `request_delay` | `0.0` | Min seconds between requests (rate limiting) |
213
+ | `display_value` | `"true"` | `sysparm_display_value` setting |
214
+ | `proxy` | `None` | HTTP/HTTPS proxy URL |
215
+ | `verify` | `True` | SSL verification (path for custom CA bundle) |
216
+
217
+ See the [full documentation](https://snowloader.readthedocs.io/en/latest/configuration.html) for all parameters.
218
+
219
+ ## Roadmap
220
+
221
+ | Version | Feature | Status |
222
+ |---------|---------|--------|
223
+ | **v0.2** | Async support (`aiohttp` + `async for`) — 10-50x faster | Coming soon |
224
+ | **v0.2** | Attachment loader (`sys_attachment` downloads) | Coming soon |
225
+ | **v0.3** | Direct vector store streaming (Pinecone, Weaviate, Chroma) | Planned |
226
+ | **v0.3** | Checkpoint and resume for large loads | Planned |
227
+ | **v1.0** | Custom field mapping for customized instances | Planned |
228
+
229
+ ## Contributing
230
+
231
+ Contributions are welcome! Please:
232
+
233
+ 1. Fork the repository
234
+ 2. Create a feature branch
235
+ 3. Write tests first (we use pytest + responses for HTTP mocking)
236
+ 4. Ensure the quality gate passes:
237
+ ```bash
238
+ ruff check src/ tests/ && ruff format --check src/ tests/ && mypy src/snowloader/ && pytest tests/ -x
239
+ ```
240
+ 5. Open a pull request
241
+
242
+ ## Author
243
+
244
+ Created and maintained by **[Roni Das](https://github.com/ronidas39)**.
245
+
246
+ ## License
247
+
248
+ MIT — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,198 @@
1
+ # snowloader
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/snowloader.svg)](https://pypi.org/project/snowloader/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/snowloader.svg)](https://pypi.org/project/snowloader/)
5
+ [![CI](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml/badge.svg)](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml)
6
+ [![Documentation](https://readthedocs.org/projects/snowloader/badge/?version=latest)](https://snowloader.readthedocs.io)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
+ [![Typed](https://img.shields.io/badge/typing-typed-blue.svg)](https://peps.python.org/pep-0561/)
9
+
10
+ > Comprehensive ServiceNow data loader for AI/LLM pipelines — Incidents, CMDB, KB, Changes, Problems, Catalog & more.
11
+
12
+ **Works with LangChain & LlamaIndex out of the box. Python 3.10–3.13.**
13
+
14
+ **[Documentation](https://snowloader.readthedocs.io)** | **[PyPI](https://pypi.org/project/snowloader/)** | **[GitHub](https://github.com/ronidas39/snowloader)**
15
+
16
+ ---
17
+
18
+ ## Why snowloader?
19
+
20
+ Building RAG or agentic AI on top of ServiceNow data? You need a reliable way to pull structured ITSM records into your vector store. Existing tools either cover a single table, ignore relationships, or lock you into one framework.
21
+
22
+ snowloader gives you:
23
+
24
+ - **6 loaders** covering the core ServiceNow tables (Incidents, Knowledge Base, CMDB, Changes, Problems, Service Catalog)
25
+ - **CMDB relationship traversal** — concurrent graph walking with dependency mapping
26
+ - **Delta sync** — only fetch records updated since your last sync
27
+ - **4 auth modes** — Basic, OAuth Password, OAuth Client Credentials, Bearer Token
28
+ - **Production-grade** — retry with backoff, rate limiting, thread safety, proxy support
29
+ - **Framework-agnostic core** with thin adapters for LangChain and LlamaIndex
30
+ - **Memory-efficient streaming** — generator-based pagination, never holds the full table in memory
31
+ - **Built-in HTML cleaning** — strips KB article HTML without extra dependencies
32
+ - **Fully typed** — PEP 561 compliant, mypy --strict clean
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ # pip
38
+ pip install snowloader # Core only
39
+ pip install snowloader[langchain] # + LangChain adapter
40
+ pip install snowloader[llamaindex] # + LlamaIndex adapter
41
+ pip install snowloader[all] # Everything
42
+
43
+ # uv
44
+ uv add snowloader
45
+ uv add snowloader[all]
46
+ ```
47
+
48
+ **Requirements:** Python 3.10+ and a ServiceNow instance with REST API access.
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ from snowloader import SnowConnection, IncidentLoader
54
+
55
+ conn = SnowConnection(
56
+ instance_url="https://mycompany.service-now.com",
57
+ username="admin",
58
+ password="password",
59
+ )
60
+
61
+ loader = IncidentLoader(connection=conn, query="active=true^priority<=2")
62
+ for doc in loader.lazy_load():
63
+ print(doc.page_content[:200])
64
+ ```
65
+
66
+ ## All 6 Loaders
67
+
68
+ Every loader shares the same interface: `load()` returns a list, `lazy_load()` yields one document at a time, `load_since(datetime)` fetches only updated records.
69
+
70
+ ```python
71
+ from snowloader import (
72
+ IncidentLoader, # IT incidents
73
+ KnowledgeBaseLoader, # KB articles (HTML auto-cleaned)
74
+ CMDBLoader, # Configuration items + relationships
75
+ ChangeLoader, # Change requests
76
+ ProblemLoader, # Problem records
77
+ CatalogLoader, # Service catalog items
78
+ )
79
+ ```
80
+
81
+ ## LangChain Adapter
82
+
83
+ ```python
84
+ from snowloader import SnowConnection
85
+ from snowloader.adapters.langchain import ServiceNowIncidentLoader
86
+
87
+ conn = SnowConnection(instance_url="...", username="...", password="...")
88
+ loader = ServiceNowIncidentLoader(connection=conn, query="active=true")
89
+ docs = loader.load() # list[langchain_core.documents.Document]
90
+
91
+ # Use with any vector store
92
+ from langchain_community.vectorstores import FAISS
93
+ from langchain_openai import OpenAIEmbeddings
94
+
95
+ vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
96
+ ```
97
+
98
+ ## LlamaIndex Adapter
99
+
100
+ ```python
101
+ from snowloader.adapters.llamaindex import ServiceNowIncidentReader
102
+
103
+ reader = ServiceNowIncidentReader(connection=conn, query="active=true")
104
+ docs = reader.load_data() # list[llama_index.core.schema.Document]
105
+
106
+ from llama_index.core import VectorStoreIndex
107
+ index = VectorStoreIndex.from_documents(docs)
108
+ ```
109
+
110
+ ## Delta Sync
111
+
112
+ ```python
113
+ from datetime import datetime, timezone
114
+
115
+ loader = IncidentLoader(connection=conn)
116
+ docs = loader.load() # First run: everything
117
+ last_sync = datetime.now(timezone.utc)
118
+
119
+ updated = loader.load_since(last_sync) # Next runs: only changes
120
+ ```
121
+
122
+ ## CMDB Relationship Traversal
123
+
124
+ ```python
125
+ loader = CMDBLoader(
126
+ connection=conn,
127
+ ci_class="cmdb_ci_server",
128
+ include_relationships=True,
129
+ )
130
+
131
+ for doc in loader.lazy_load():
132
+ # -> db-prod-01 (Depends on::Used by)
133
+ # <- load-balancer-01 (Depends on::Used by)
134
+ print(doc.page_content)
135
+ ```
136
+
137
+ ## Authentication
138
+
139
+ ```python
140
+ # Basic Auth (development)
141
+ conn = SnowConnection(instance_url="...", username="admin", password="pass")
142
+
143
+ # OAuth Client Credentials (recommended for production)
144
+ conn = SnowConnection(instance_url="...", client_id="...", client_secret="...")
145
+
146
+ # OAuth Password Grant
147
+ conn = SnowConnection(instance_url="...", client_id="...", client_secret="...",
148
+ username="...", password="...")
149
+
150
+ # Bearer Token (pre-obtained)
151
+ conn = SnowConnection(instance_url="...", token="eyJhbG...")
152
+ ```
153
+
154
+ ## Configuration
155
+
156
+ | Parameter | Default | Description |
157
+ |-----------|---------|-------------|
158
+ | `page_size` | `100` | Records per API call (1–10,000) |
159
+ | `timeout` | `60` | HTTP timeout in seconds |
160
+ | `max_retries` | `3` | Retry attempts for 429/502/503/504 |
161
+ | `retry_backoff` | `1.0` | Base delay between retries (doubles each attempt) |
162
+ | `request_delay` | `0.0` | Min seconds between requests (rate limiting) |
163
+ | `display_value` | `"true"` | `sysparm_display_value` setting |
164
+ | `proxy` | `None` | HTTP/HTTPS proxy URL |
165
+ | `verify` | `True` | SSL verification (path for custom CA bundle) |
166
+
167
+ See the [full documentation](https://snowloader.readthedocs.io/en/latest/configuration.html) for all parameters.
168
+
169
+ ## Roadmap
170
+
171
+ | Version | Feature | Status |
172
+ |---------|---------|--------|
173
+ | **v0.2** | Async support (`aiohttp` + `async for`) — 10-50x faster | Coming soon |
174
+ | **v0.2** | Attachment loader (`sys_attachment` downloads) | Coming soon |
175
+ | **v0.3** | Direct vector store streaming (Pinecone, Weaviate, Chroma) | Planned |
176
+ | **v0.3** | Checkpoint and resume for large loads | Planned |
177
+ | **v1.0** | Custom field mapping for customized instances | Planned |
178
+
179
+ ## Contributing
180
+
181
+ Contributions are welcome! Please:
182
+
183
+ 1. Fork the repository
184
+ 2. Create a feature branch
185
+ 3. Write tests first (we use pytest + responses for HTTP mocking)
186
+ 4. Ensure the quality gate passes:
187
+ ```bash
188
+ ruff check src/ tests/ && ruff format --check src/ tests/ && mypy src/snowloader/ && pytest tests/ -x
189
+ ```
190
+ 5. Open a pull request
191
+
192
+ ## Author
193
+
194
+ Created and maintained by **[Roni Das](https://github.com/ronidas39)**.
195
+
196
+ ## License
197
+
198
+ MIT — see [LICENSE](LICENSE) for details.