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.
- snowloader-0.1.0/.github/workflows/ci.yml +80 -0
- snowloader-0.1.0/.github/workflows/publish.yml +52 -0
- snowloader-0.1.0/.gitignore +28 -0
- snowloader-0.1.0/.readthedocs.yaml +17 -0
- snowloader-0.1.0/CHANGELOG.md +44 -0
- snowloader-0.1.0/LICENSE +21 -0
- snowloader-0.1.0/PKG-INFO +248 -0
- snowloader-0.1.0/README.md +198 -0
- snowloader-0.1.0/docs/adapters.rst +124 -0
- snowloader-0.1.0/docs/advanced.rst +161 -0
- snowloader-0.1.0/docs/api.rst +63 -0
- snowloader-0.1.0/docs/authentication.rst +133 -0
- snowloader-0.1.0/docs/changelog.rst +50 -0
- snowloader-0.1.0/docs/conf.py +91 -0
- snowloader-0.1.0/docs/configuration.rst +159 -0
- snowloader-0.1.0/docs/getting-started.rst +147 -0
- snowloader-0.1.0/docs/index.rst +55 -0
- snowloader-0.1.0/docs/loaders.rst +139 -0
- snowloader-0.1.0/docs/roadmap.rst +85 -0
- snowloader-0.1.0/docs/servicenow_api.md +92 -0
- snowloader-0.1.0/examples/01_basic_incidents.py +46 -0
- snowloader-0.1.0/examples/02_langchain_rag.py +56 -0
- snowloader-0.1.0/examples/03_llamaindex_rag.py +50 -0
- snowloader-0.1.0/examples/04_delta_sync.py +70 -0
- snowloader-0.1.0/examples/05_cmdb_graph.py +58 -0
- snowloader-0.1.0/pyproject.toml +100 -0
- snowloader-0.1.0/src/snowloader/__init__.py +36 -0
- snowloader-0.1.0/src/snowloader/adapters/__init__.py +16 -0
- snowloader-0.1.0/src/snowloader/adapters/langchain.py +110 -0
- snowloader-0.1.0/src/snowloader/adapters/llamaindex.py +127 -0
- snowloader-0.1.0/src/snowloader/connection.py +670 -0
- snowloader-0.1.0/src/snowloader/loaders/__init__.py +25 -0
- snowloader-0.1.0/src/snowloader/loaders/_field_utils.py +88 -0
- snowloader-0.1.0/src/snowloader/loaders/catalog.py +94 -0
- snowloader-0.1.0/src/snowloader/loaders/changes.py +132 -0
- snowloader-0.1.0/src/snowloader/loaders/cmdb.py +268 -0
- snowloader-0.1.0/src/snowloader/loaders/incidents.py +158 -0
- snowloader-0.1.0/src/snowloader/loaders/knowledge_base.py +103 -0
- snowloader-0.1.0/src/snowloader/loaders/problems.py +137 -0
- snowloader-0.1.0/src/snowloader/models.py +260 -0
- snowloader-0.1.0/src/snowloader/py.typed +0 -0
- snowloader-0.1.0/src/snowloader/utils/__init__.py +14 -0
- snowloader-0.1.0/src/snowloader/utils/html_cleaner.py +73 -0
- snowloader-0.1.0/tests/__init__.py +1 -0
- snowloader-0.1.0/tests/integration/__init__.py +5 -0
- snowloader-0.1.0/tests/integration/test_live.py +579 -0
- snowloader-0.1.0/tests/unit/__init__.py +1 -0
- snowloader-0.1.0/tests/unit/test_catalog.py +101 -0
- snowloader-0.1.0/tests/unit/test_changes.py +142 -0
- snowloader-0.1.0/tests/unit/test_cmdb.py +316 -0
- snowloader-0.1.0/tests/unit/test_connection.py +314 -0
- snowloader-0.1.0/tests/unit/test_html_cleaner.py +56 -0
- snowloader-0.1.0/tests/unit/test_incidents.py +235 -0
- snowloader-0.1.0/tests/unit/test_kb.py +199 -0
- snowloader-0.1.0/tests/unit/test_langchain_adapter.py +175 -0
- snowloader-0.1.0/tests/unit/test_llamaindex_adapter.py +171 -0
- snowloader-0.1.0/tests/unit/test_models.py +232 -0
- snowloader-0.1.0/tests/unit/test_problems.py +129 -0
- 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,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
|
snowloader-0.1.0/LICENSE
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/snowloader/)
|
|
54
|
+
[](https://pypi.org/project/snowloader/)
|
|
55
|
+
[](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml)
|
|
56
|
+
[](https://snowloader.readthedocs.io)
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
[](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
|
+
[](https://pypi.org/project/snowloader/)
|
|
4
|
+
[](https://pypi.org/project/snowloader/)
|
|
5
|
+
[](https://github.com/ronidas39/snowloader/actions/workflows/ci.yml)
|
|
6
|
+
[](https://snowloader.readthedocs.io)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](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.
|