gurucloud-kb 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.
- gurucloud_kb-0.1.0/.gitignore +90 -0
- gurucloud_kb-0.1.0/LICENSE +21 -0
- gurucloud_kb-0.1.0/PKG-INFO +184 -0
- gurucloud_kb-0.1.0/README.md +161 -0
- gurucloud_kb-0.1.0/pyproject.toml +41 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/__init__.py +90 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/_async_http.py +112 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/_http.py +112 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/async_client.py +187 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/async_kb.py +279 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/client.py +196 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/errors.py +61 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/kb.py +290 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/py.typed +0 -0
- gurucloud_kb-0.1.0/src/gurucloud_kb/types.py +254 -0
- gurucloud_kb-0.1.0/tests/test_async_client.py +475 -0
- gurucloud_kb-0.1.0/tests/test_client.py +453 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
env/
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
.playwright-mcp/
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
|
|
26
|
+
# Virtual Environment
|
|
27
|
+
venv/
|
|
28
|
+
ENV/
|
|
29
|
+
.env
|
|
30
|
+
|
|
31
|
+
# IDE
|
|
32
|
+
.idea/
|
|
33
|
+
.vscode/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
|
|
37
|
+
# Claude Code MCP config (contains user-specific tokens)
|
|
38
|
+
.mcp.json
|
|
39
|
+
|
|
40
|
+
# Docker
|
|
41
|
+
docker-compose.override.yml
|
|
42
|
+
|
|
43
|
+
# Database
|
|
44
|
+
*.sqlite3
|
|
45
|
+
*.db
|
|
46
|
+
|
|
47
|
+
# Project specific
|
|
48
|
+
application_default_credentials.json
|
|
49
|
+
*.log
|
|
50
|
+
local_cache/
|
|
51
|
+
local_files/
|
|
52
|
+
|
|
53
|
+
# Gmail API credentials (generated via admin UI)
|
|
54
|
+
credentials.json
|
|
55
|
+
token.json
|
|
56
|
+
|
|
57
|
+
# Logs directory
|
|
58
|
+
logs/
|
|
59
|
+
|
|
60
|
+
# OS specific
|
|
61
|
+
.DS_Store
|
|
62
|
+
Thumbs.db
|
|
63
|
+
|
|
64
|
+
# Secrets and credentials
|
|
65
|
+
*.pem
|
|
66
|
+
*.key
|
|
67
|
+
secrets/
|
|
68
|
+
|
|
69
|
+
# Initialization tracking
|
|
70
|
+
initialization_scripts/initialization_status.json
|
|
71
|
+
|
|
72
|
+
# Certbot/Let's Encrypt files (contain sensitive account data)
|
|
73
|
+
certbot/conf/accounts/
|
|
74
|
+
certbot/conf/live/
|
|
75
|
+
certbot/conf/renewal/
|
|
76
|
+
certbot/conf/archive/
|
|
77
|
+
certbot/conf/keys/
|
|
78
|
+
|
|
79
|
+
# Node.js
|
|
80
|
+
node_modules/
|
|
81
|
+
npm-debug.log*
|
|
82
|
+
yarn-debug.log*
|
|
83
|
+
yarn-error.log*
|
|
84
|
+
.npm/
|
|
85
|
+
.yarn/
|
|
86
|
+
|
|
87
|
+
gcloud_credentials.json
|
|
88
|
+
*.glb
|
|
89
|
+
*.blend
|
|
90
|
+
*.blend1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GuruCloud AI
|
|
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,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gurucloud-kb
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the GuruCloud Knowledge Bank API
|
|
5
|
+
Author-email: GuruCloud AI <support@gurucloudai.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: httpx>=0.25.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# GuruCloud KB SDK
|
|
25
|
+
|
|
26
|
+
Python SDK for the [GuruCloud Knowledge Bank API](https://www.gurucloudai.com/docs/kb).
|
|
27
|
+
|
|
28
|
+
**Full documentation**: [gurucloudai.com/docs/kb](https://www.gurucloudai.com/docs/kb)
|
|
29
|
+
**OpenAPI spec**: [gurucloudai.com/docs/kb/openapi.json](https://www.gurucloudai.com/docs/kb/openapi.json)
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install gurucloud-kb
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from gurucloud_kb import GuruCloudClient
|
|
41
|
+
|
|
42
|
+
client = GuruCloudClient(api_key="kb_your_api_key")
|
|
43
|
+
|
|
44
|
+
# List your Knowledge Banks
|
|
45
|
+
kbs = client.list_kbs()
|
|
46
|
+
|
|
47
|
+
# Work with a specific KB
|
|
48
|
+
kb = client.get_kb("your-kb-uuid")
|
|
49
|
+
|
|
50
|
+
# Search
|
|
51
|
+
results = kb.search("how does authentication work?")
|
|
52
|
+
for r in results:
|
|
53
|
+
print(r["content"], r["combined_score"])
|
|
54
|
+
|
|
55
|
+
# Add an entry
|
|
56
|
+
kb.add_entry({
|
|
57
|
+
"dimensions": {
|
|
58
|
+
"content": "The auth service uses JWT tokens with RS256 signing.",
|
|
59
|
+
"useful_for": "Understanding authentication architecture",
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Async Support
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from gurucloud_kb import AsyncGuruCloudClient
|
|
68
|
+
|
|
69
|
+
async with AsyncGuruCloudClient(api_key="kb_your_api_key") as client:
|
|
70
|
+
kb = await client.get_kb("your-kb-uuid")
|
|
71
|
+
results = await kb.search("deployment process")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Key Features
|
|
75
|
+
|
|
76
|
+
- **Sync & async clients** — `GuruCloudClient` and `AsyncGuruCloudClient`
|
|
77
|
+
- **Knowledge Bank CRUD** — create, list, update, delete KBs
|
|
78
|
+
- **Entry management** — add, update, delete, batch ingest entries
|
|
79
|
+
- **Semantic search** — single-query or multi-dimensional weighted search
|
|
80
|
+
- **Schema management** — get, update, validate dimension schemas
|
|
81
|
+
- **MCP integration** — get MCP server definitions for agent injection
|
|
82
|
+
- **Deduplication events** — inspect how entries were deduplicated
|
|
83
|
+
- **Event logs** — trace entry processing lifecycle
|
|
84
|
+
- **API key management** — create, list, delete API keys
|
|
85
|
+
- **Typed responses** — all methods return TypedDict types for IDE autocomplete
|
|
86
|
+
|
|
87
|
+
## Entry Management
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# List entries
|
|
91
|
+
entries = kb.list_entries(limit=20, offset=0)
|
|
92
|
+
|
|
93
|
+
# Get a single entry
|
|
94
|
+
entry = kb.get_entry("entry-uuid")
|
|
95
|
+
|
|
96
|
+
# Update an entry
|
|
97
|
+
kb.update_entry("entry-uuid", {"dimensions": {"content": "Updated content"}})
|
|
98
|
+
|
|
99
|
+
# Delete
|
|
100
|
+
kb.delete_entry("entry-uuid")
|
|
101
|
+
|
|
102
|
+
# Batch ingest with deduplication
|
|
103
|
+
result = kb.ingest([
|
|
104
|
+
{"dimensions": {"content": "Fact 1", "useful_for": "Context 1"}},
|
|
105
|
+
{"dimensions": {"content": "Fact 2", "useful_for": "Context 2"}},
|
|
106
|
+
], deduplicate=True)
|
|
107
|
+
print(f"Ingested: {result['ingested']}")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Search
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Simple string search (searches the "content" dimension)
|
|
114
|
+
results = kb.search("authentication flow", k=5, threshold=0.6)
|
|
115
|
+
|
|
116
|
+
# Multi-dimensional weighted search
|
|
117
|
+
results = kb.search({
|
|
118
|
+
"dimensions": {
|
|
119
|
+
"content": {"query_text": "JWT tokens", "weight": 0.7},
|
|
120
|
+
"useful_for": {"query_text": "security audit", "weight": 0.3},
|
|
121
|
+
},
|
|
122
|
+
"k": 10,
|
|
123
|
+
"threshold": 0.5,
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## MCP Integration
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Get MCP server definition for agent injection
|
|
131
|
+
mcp_def = kb.get_mcp_server_definition()
|
|
132
|
+
|
|
133
|
+
# Use in agent config:
|
|
134
|
+
mcp_config = {
|
|
135
|
+
"type": "http",
|
|
136
|
+
"url": mcp_def["url"],
|
|
137
|
+
"headers": {"Authorization": f"Bearer {mcp_def['token']}"}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Deduplication Events
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
# List dedup events
|
|
145
|
+
events = kb.list_events(action="conflict", limit=20)
|
|
146
|
+
|
|
147
|
+
# Get full event details
|
|
148
|
+
event = kb.get_event("event-uuid")
|
|
149
|
+
print(event["reasoning"], event["action"])
|
|
150
|
+
|
|
151
|
+
# Entry processing logs
|
|
152
|
+
logs = kb.list_event_logs(event_type="dedup", limit=50)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Error Handling
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from gurucloud_kb import (
|
|
159
|
+
GuruCloudError,
|
|
160
|
+
AuthenticationError,
|
|
161
|
+
NotFoundError,
|
|
162
|
+
RateLimitError,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
kb = client.get_kb("bad-uuid")
|
|
167
|
+
except NotFoundError:
|
|
168
|
+
print("KB not found")
|
|
169
|
+
except AuthenticationError:
|
|
170
|
+
print("Invalid API key")
|
|
171
|
+
except RateLimitError:
|
|
172
|
+
print("Rate limited — slow down")
|
|
173
|
+
except GuruCloudError as e:
|
|
174
|
+
print(f"API error: {e.message}")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Requirements
|
|
178
|
+
|
|
179
|
+
- Python 3.10+
|
|
180
|
+
- `httpx` >= 0.25.0
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# GuruCloud KB SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for the [GuruCloud Knowledge Bank API](https://www.gurucloudai.com/docs/kb).
|
|
4
|
+
|
|
5
|
+
**Full documentation**: [gurucloudai.com/docs/kb](https://www.gurucloudai.com/docs/kb)
|
|
6
|
+
**OpenAPI spec**: [gurucloudai.com/docs/kb/openapi.json](https://www.gurucloudai.com/docs/kb/openapi.json)
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install gurucloud-kb
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from gurucloud_kb import GuruCloudClient
|
|
18
|
+
|
|
19
|
+
client = GuruCloudClient(api_key="kb_your_api_key")
|
|
20
|
+
|
|
21
|
+
# List your Knowledge Banks
|
|
22
|
+
kbs = client.list_kbs()
|
|
23
|
+
|
|
24
|
+
# Work with a specific KB
|
|
25
|
+
kb = client.get_kb("your-kb-uuid")
|
|
26
|
+
|
|
27
|
+
# Search
|
|
28
|
+
results = kb.search("how does authentication work?")
|
|
29
|
+
for r in results:
|
|
30
|
+
print(r["content"], r["combined_score"])
|
|
31
|
+
|
|
32
|
+
# Add an entry
|
|
33
|
+
kb.add_entry({
|
|
34
|
+
"dimensions": {
|
|
35
|
+
"content": "The auth service uses JWT tokens with RS256 signing.",
|
|
36
|
+
"useful_for": "Understanding authentication architecture",
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Async Support
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from gurucloud_kb import AsyncGuruCloudClient
|
|
45
|
+
|
|
46
|
+
async with AsyncGuruCloudClient(api_key="kb_your_api_key") as client:
|
|
47
|
+
kb = await client.get_kb("your-kb-uuid")
|
|
48
|
+
results = await kb.search("deployment process")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Key Features
|
|
52
|
+
|
|
53
|
+
- **Sync & async clients** — `GuruCloudClient` and `AsyncGuruCloudClient`
|
|
54
|
+
- **Knowledge Bank CRUD** — create, list, update, delete KBs
|
|
55
|
+
- **Entry management** — add, update, delete, batch ingest entries
|
|
56
|
+
- **Semantic search** — single-query or multi-dimensional weighted search
|
|
57
|
+
- **Schema management** — get, update, validate dimension schemas
|
|
58
|
+
- **MCP integration** — get MCP server definitions for agent injection
|
|
59
|
+
- **Deduplication events** — inspect how entries were deduplicated
|
|
60
|
+
- **Event logs** — trace entry processing lifecycle
|
|
61
|
+
- **API key management** — create, list, delete API keys
|
|
62
|
+
- **Typed responses** — all methods return TypedDict types for IDE autocomplete
|
|
63
|
+
|
|
64
|
+
## Entry Management
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# List entries
|
|
68
|
+
entries = kb.list_entries(limit=20, offset=0)
|
|
69
|
+
|
|
70
|
+
# Get a single entry
|
|
71
|
+
entry = kb.get_entry("entry-uuid")
|
|
72
|
+
|
|
73
|
+
# Update an entry
|
|
74
|
+
kb.update_entry("entry-uuid", {"dimensions": {"content": "Updated content"}})
|
|
75
|
+
|
|
76
|
+
# Delete
|
|
77
|
+
kb.delete_entry("entry-uuid")
|
|
78
|
+
|
|
79
|
+
# Batch ingest with deduplication
|
|
80
|
+
result = kb.ingest([
|
|
81
|
+
{"dimensions": {"content": "Fact 1", "useful_for": "Context 1"}},
|
|
82
|
+
{"dimensions": {"content": "Fact 2", "useful_for": "Context 2"}},
|
|
83
|
+
], deduplicate=True)
|
|
84
|
+
print(f"Ingested: {result['ingested']}")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Search
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# Simple string search (searches the "content" dimension)
|
|
91
|
+
results = kb.search("authentication flow", k=5, threshold=0.6)
|
|
92
|
+
|
|
93
|
+
# Multi-dimensional weighted search
|
|
94
|
+
results = kb.search({
|
|
95
|
+
"dimensions": {
|
|
96
|
+
"content": {"query_text": "JWT tokens", "weight": 0.7},
|
|
97
|
+
"useful_for": {"query_text": "security audit", "weight": 0.3},
|
|
98
|
+
},
|
|
99
|
+
"k": 10,
|
|
100
|
+
"threshold": 0.5,
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## MCP Integration
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# Get MCP server definition for agent injection
|
|
108
|
+
mcp_def = kb.get_mcp_server_definition()
|
|
109
|
+
|
|
110
|
+
# Use in agent config:
|
|
111
|
+
mcp_config = {
|
|
112
|
+
"type": "http",
|
|
113
|
+
"url": mcp_def["url"],
|
|
114
|
+
"headers": {"Authorization": f"Bearer {mcp_def['token']}"}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Deduplication Events
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# List dedup events
|
|
122
|
+
events = kb.list_events(action="conflict", limit=20)
|
|
123
|
+
|
|
124
|
+
# Get full event details
|
|
125
|
+
event = kb.get_event("event-uuid")
|
|
126
|
+
print(event["reasoning"], event["action"])
|
|
127
|
+
|
|
128
|
+
# Entry processing logs
|
|
129
|
+
logs = kb.list_event_logs(event_type="dedup", limit=50)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Error Handling
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from gurucloud_kb import (
|
|
136
|
+
GuruCloudError,
|
|
137
|
+
AuthenticationError,
|
|
138
|
+
NotFoundError,
|
|
139
|
+
RateLimitError,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
kb = client.get_kb("bad-uuid")
|
|
144
|
+
except NotFoundError:
|
|
145
|
+
print("KB not found")
|
|
146
|
+
except AuthenticationError:
|
|
147
|
+
print("Invalid API key")
|
|
148
|
+
except RateLimitError:
|
|
149
|
+
print("Rate limited — slow down")
|
|
150
|
+
except GuruCloudError as e:
|
|
151
|
+
print(f"API error: {e.message}")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Requirements
|
|
155
|
+
|
|
156
|
+
- Python 3.10+
|
|
157
|
+
- `httpx` >= 0.25.0
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gurucloud-kb"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the GuruCloud Knowledge Bank API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "GuruCloud AI", email = "support@gurucloudai.com" },
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.25.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=7.0",
|
|
32
|
+
"pytest-asyncio>=0.21",
|
|
33
|
+
"respx>=0.21",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/gurucloud_kb"]
|
|
38
|
+
|
|
39
|
+
[tool.pyright]
|
|
40
|
+
pythonVersion = "3.10"
|
|
41
|
+
typeCheckingMode = "strict"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""GuruCloud Knowledge Bank SDK.
|
|
2
|
+
|
|
3
|
+
Example::
|
|
4
|
+
|
|
5
|
+
from gurucloud_kb import GuruCloudClient
|
|
6
|
+
|
|
7
|
+
client = GuruCloudClient(api_key="kb_abc123...")
|
|
8
|
+
|
|
9
|
+
# List all KBs
|
|
10
|
+
kbs = client.list_kbs()
|
|
11
|
+
|
|
12
|
+
# Work with a specific KB
|
|
13
|
+
kb = client.get_kb("my-kb-uuid")
|
|
14
|
+
results = kb.search("how does auth work?")
|
|
15
|
+
|
|
16
|
+
# Get MCP server definition for agent injection
|
|
17
|
+
mcp_def = kb.get_mcp_server_definition()
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from gurucloud_kb.async_client import AsyncGuruCloudClient
|
|
21
|
+
from gurucloud_kb.async_kb import AsyncKnowledgeBank
|
|
22
|
+
from gurucloud_kb.client import GuruCloudClient
|
|
23
|
+
from gurucloud_kb.errors import (
|
|
24
|
+
APIError,
|
|
25
|
+
AuthenticationError,
|
|
26
|
+
ConnectionError,
|
|
27
|
+
GuruCloudError,
|
|
28
|
+
NotFoundError,
|
|
29
|
+
PermissionError,
|
|
30
|
+
RateLimitError,
|
|
31
|
+
)
|
|
32
|
+
from gurucloud_kb.kb import KnowledgeBank
|
|
33
|
+
from gurucloud_kb.types import (
|
|
34
|
+
APIKeyInfo,
|
|
35
|
+
BatchIngestResult,
|
|
36
|
+
CategoryConfig,
|
|
37
|
+
DeduplicationEvent,
|
|
38
|
+
DeduplicationEventList,
|
|
39
|
+
DeduplicationEventSummary,
|
|
40
|
+
DimensionConfig,
|
|
41
|
+
DimensionQuery,
|
|
42
|
+
DimensionSchema,
|
|
43
|
+
EntryEventLog,
|
|
44
|
+
EntryEventLogList,
|
|
45
|
+
EntryInput,
|
|
46
|
+
EntryResult,
|
|
47
|
+
KBInfo,
|
|
48
|
+
MCPServerDefinition,
|
|
49
|
+
SchemaWarning,
|
|
50
|
+
SearchRequest,
|
|
51
|
+
SearchResult,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
# Sync client
|
|
56
|
+
"GuruCloudClient",
|
|
57
|
+
"KnowledgeBank",
|
|
58
|
+
# Async client
|
|
59
|
+
"AsyncGuruCloudClient",
|
|
60
|
+
"AsyncKnowledgeBank",
|
|
61
|
+
# Errors
|
|
62
|
+
"GuruCloudError",
|
|
63
|
+
"APIError",
|
|
64
|
+
"AuthenticationError",
|
|
65
|
+
"PermissionError",
|
|
66
|
+
"NotFoundError",
|
|
67
|
+
"RateLimitError",
|
|
68
|
+
"ConnectionError",
|
|
69
|
+
# Types
|
|
70
|
+
"KBInfo",
|
|
71
|
+
"DimensionConfig",
|
|
72
|
+
"CategoryConfig",
|
|
73
|
+
"DimensionSchema",
|
|
74
|
+
"SchemaWarning",
|
|
75
|
+
"EntryInput",
|
|
76
|
+
"EntryResult",
|
|
77
|
+
"DimensionQuery",
|
|
78
|
+
"SearchRequest",
|
|
79
|
+
"SearchResult",
|
|
80
|
+
"MCPServerDefinition",
|
|
81
|
+
"APIKeyInfo",
|
|
82
|
+
"BatchIngestResult",
|
|
83
|
+
"DeduplicationEvent",
|
|
84
|
+
"DeduplicationEventSummary",
|
|
85
|
+
"DeduplicationEventList",
|
|
86
|
+
"EntryEventLog",
|
|
87
|
+
"EntryEventLogList",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Async HTTP transport for the GuruCloud KB SDK.
|
|
2
|
+
|
|
3
|
+
Wraps httpx.AsyncClient to provide:
|
|
4
|
+
- Automatic Bearer token injection
|
|
5
|
+
- Response envelope unwrapping (``{"data": ...}``)
|
|
6
|
+
- Typed error raising
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
from gurucloud_kb.errors import (
|
|
16
|
+
APIError,
|
|
17
|
+
AuthenticationError,
|
|
18
|
+
ConnectionError,
|
|
19
|
+
NotFoundError,
|
|
20
|
+
PermissionError,
|
|
21
|
+
RateLimitError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
_DEFAULT_TIMEOUT = 30.0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AsyncHTTPClient:
|
|
28
|
+
"""Async wrapper around httpx for the KB API."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
base_url: str,
|
|
33
|
+
api_key: str,
|
|
34
|
+
timeout: float = _DEFAULT_TIMEOUT,
|
|
35
|
+
) -> None:
|
|
36
|
+
self._base_url = base_url.rstrip("/")
|
|
37
|
+
self._client = httpx.AsyncClient(
|
|
38
|
+
base_url=f"{self._base_url}/api/v1/kb",
|
|
39
|
+
headers={
|
|
40
|
+
"Authorization": f"Bearer {api_key}",
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
},
|
|
43
|
+
timeout=timeout,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# ── public verbs ────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
async def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
49
|
+
return await self._request("GET", path, params=params)
|
|
50
|
+
|
|
51
|
+
async def post(self, path: str, json: Any = None) -> Any:
|
|
52
|
+
return await self._request("POST", path, json=json)
|
|
53
|
+
|
|
54
|
+
async def put(self, path: str, json: Any = None) -> Any:
|
|
55
|
+
return await self._request("PUT", path, json=json)
|
|
56
|
+
|
|
57
|
+
async def patch(self, path: str, json: Any = None) -> Any:
|
|
58
|
+
return await self._request("PATCH", path, json=json)
|
|
59
|
+
|
|
60
|
+
async def delete(self, path: str) -> Any:
|
|
61
|
+
return await self._request("DELETE", path)
|
|
62
|
+
|
|
63
|
+
async def close(self) -> None:
|
|
64
|
+
await self._client.aclose()
|
|
65
|
+
|
|
66
|
+
# ── internals ───────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
async def _request(
|
|
69
|
+
self,
|
|
70
|
+
method: str,
|
|
71
|
+
path: str,
|
|
72
|
+
params: dict[str, Any] | None = None,
|
|
73
|
+
json: Any = None,
|
|
74
|
+
) -> Any:
|
|
75
|
+
try:
|
|
76
|
+
resp = await self._client.request(method, path, params=params, json=json)
|
|
77
|
+
except httpx.ConnectError as exc:
|
|
78
|
+
raise ConnectionError(f"Cannot reach {self._base_url}: {exc}") from exc
|
|
79
|
+
except httpx.TimeoutException as exc:
|
|
80
|
+
raise ConnectionError(f"Request timed out: {exc}") from exc
|
|
81
|
+
|
|
82
|
+
if resp.status_code >= 400:
|
|
83
|
+
self._raise_for_status(resp)
|
|
84
|
+
|
|
85
|
+
# The API wraps successful responses in {"data": ...}
|
|
86
|
+
body = resp.json()
|
|
87
|
+
if isinstance(body, dict) and "data" in body:
|
|
88
|
+
return body["data"]
|
|
89
|
+
return body
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def _raise_for_status(resp: httpx.Response) -> None:
|
|
93
|
+
"""Map HTTP error responses to typed SDK exceptions."""
|
|
94
|
+
try:
|
|
95
|
+
body = resp.json()
|
|
96
|
+
except Exception:
|
|
97
|
+
raise APIError(resp.status_code, "unknown", resp.text)
|
|
98
|
+
|
|
99
|
+
error = body.get("error", {})
|
|
100
|
+
code = error.get("code", "unknown") if isinstance(error, dict) else "unknown"
|
|
101
|
+
message = error.get("message", resp.text) if isinstance(error, dict) else str(error)
|
|
102
|
+
|
|
103
|
+
status = resp.status_code
|
|
104
|
+
if status == 401:
|
|
105
|
+
raise AuthenticationError(code, message)
|
|
106
|
+
if status == 403:
|
|
107
|
+
raise PermissionError(code, message)
|
|
108
|
+
if status == 404:
|
|
109
|
+
raise NotFoundError(code, message)
|
|
110
|
+
if status == 429:
|
|
111
|
+
raise RateLimitError(code, message)
|
|
112
|
+
raise APIError(status, code, message)
|