vaultkit 0.3.1__tar.gz → 0.4.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.
- {vaultkit-0.3.1 → vaultkit-0.4.0}/PKG-INFO +19 -1
- {vaultkit-0.3.1 → vaultkit-0.4.0}/README.md +18 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/pyproject.toml +1 -1
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/__init__.py +1 -1
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/client.py +8 -3
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/definitions.py +34 -4
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/utils/validation.py +47 -2
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/PKG-INFO +19 -1
- {vaultkit-0.3.1 → vaultkit-0.4.0}/setup.cfg +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/tests/test_fetch.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/tests/test_query.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/core/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/core/http.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/core/polling.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/errors/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/errors/base.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/errors/exceptions.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/mcp/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/mcp/__main__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/mcp/identity.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/mcp/server.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/models/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/models/dataset_info.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/models/dataset_schema.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/models/fetch_result.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/models/query_result.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/adapters/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/adapters/anthropic.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/adapters/mcp.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/adapters/openai.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/builder.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/executor.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/tools/schemas.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/utils/__init__.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit/utils/retry.py +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/SOURCES.txt +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/dependency_links.txt +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/entry_points.txt +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/requires.txt +0 -0
- {vaultkit-0.3.1 → vaultkit-0.4.0}/vaultkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vaultkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: VaultKit Python SDK for policy-driven, runtime governed data access
|
|
5
5
|
Author-email: VaultKit <founders@vaultkit.io>
|
|
6
6
|
License: MIT
|
|
@@ -93,6 +93,24 @@ result = client.execute(
|
|
|
93
93
|
|
|
94
94
|
- `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
|
|
95
95
|
- `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
|
|
96
|
+
An entry can also be a time bucket of a timestamp or date field, which groups by period
|
|
97
|
+
(`day`, `week`, `month`, `quarter` or `year`; `alias` and `timezone` are optional, and
|
|
98
|
+
the time zone defaults to UTC). Use one bucketed query for a trend instead of one query
|
|
99
|
+
per period:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
client.execute(
|
|
103
|
+
dataset="payments",
|
|
104
|
+
aggregates=[{"func": "count", "field": "*", "alias": "failures"}],
|
|
105
|
+
filters=[{"field": "status", "operator": "eq", "value": "failed"}],
|
|
106
|
+
group_by=[{"field": "created_at", "bucket": "quarter", "alias": "quarter"}],
|
|
107
|
+
order_by={"column": "quarter"},
|
|
108
|
+
purpose="Failed payments per quarter",
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Bucketing a fully or partially masked field is denied, like grouping by it. Needs a
|
|
113
|
+
control plane with time buckets (vaultkit#106); older ones reject the request.
|
|
96
114
|
- `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
|
|
97
115
|
- `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
|
|
98
116
|
- `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
|
|
@@ -67,6 +67,24 @@ result = client.execute(
|
|
|
67
67
|
|
|
68
68
|
- `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
|
|
69
69
|
- `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
|
|
70
|
+
An entry can also be a time bucket of a timestamp or date field, which groups by period
|
|
71
|
+
(`day`, `week`, `month`, `quarter` or `year`; `alias` and `timezone` are optional, and
|
|
72
|
+
the time zone defaults to UTC). Use one bucketed query for a trend instead of one query
|
|
73
|
+
per period:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
client.execute(
|
|
77
|
+
dataset="payments",
|
|
78
|
+
aggregates=[{"func": "count", "field": "*", "alias": "failures"}],
|
|
79
|
+
filters=[{"field": "status", "operator": "eq", "value": "failed"}],
|
|
80
|
+
group_by=[{"field": "created_at", "bucket": "quarter", "alias": "quarter"}],
|
|
81
|
+
order_by={"column": "quarter"},
|
|
82
|
+
purpose="Failed payments per quarter",
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Bucketing a fully or partially masked field is denied, like grouping by it. Needs a
|
|
87
|
+
control plane with time buckets (vaultkit#106); older ones reject the request.
|
|
70
88
|
- `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
|
|
71
89
|
- `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
|
|
72
90
|
- `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vaultkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "VaultKit Python SDK for policy-driven, runtime governed data access"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "VaultKit", email = "founders@vaultkit.io" }
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import Any, Dict, List, Optional
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
|
6
6
|
|
|
7
7
|
from vaultkit.core.http import HttpClient, HttpConfig
|
|
8
8
|
from vaultkit.core.polling import PollConfig, poll_until_done
|
|
@@ -103,7 +103,7 @@ class VaultKitClient:
|
|
|
103
103
|
filters: Optional[List[Dict[str, Any]]] = None,
|
|
104
104
|
limit: Optional[int] = None,
|
|
105
105
|
aggregates: Optional[List[Dict[str, Any]]] = None,
|
|
106
|
-
group_by: Optional[List[str]] = None,
|
|
106
|
+
group_by: Optional[List[Union[str, Dict[str, str]]]] = None,
|
|
107
107
|
having: Optional[List[Dict[str, Any]]] = None,
|
|
108
108
|
order_by: Optional[Dict[str, Any]] = None,
|
|
109
109
|
purpose: Optional[str] = None,
|
|
@@ -118,6 +118,11 @@ class VaultKitClient:
|
|
|
118
118
|
aggregates [{"func": "sum", "field": "amount", "alias": "total"}]
|
|
119
119
|
func is one of count, sum, avg, min, max; "*" only with count.
|
|
120
120
|
group_by ["region"] — every field in `fields` must be listed when given.
|
|
121
|
+
A time bucket of a timestamp or date field groups by period:
|
|
122
|
+
{"field": "created_at", "bucket": "quarter", "alias": "quarter",
|
|
123
|
+
"timezone": "America/Toronto"}; bucket is one of day, week,
|
|
124
|
+
month, quarter, year; alias and timezone (default UTC) are
|
|
125
|
+
optional. One bucketed query replaces one query per period.
|
|
121
126
|
having [{"field": "SUM(amount)", "operator": "gt", "value": 1000}]
|
|
122
127
|
field must be an aggregate expression, not an alias.
|
|
123
128
|
order_by {"column": "total", "direction": "desc"}
|
|
@@ -240,7 +245,7 @@ class VaultKitClient:
|
|
|
240
245
|
filters: Optional[List[Dict[str, Any]]] = None,
|
|
241
246
|
limit: Optional[int] = None,
|
|
242
247
|
aggregates: Optional[List[Dict[str, Any]]] = None,
|
|
243
|
-
group_by: Optional[List[str]] = None,
|
|
248
|
+
group_by: Optional[List[Union[str, Dict[str, str]]]] = None,
|
|
244
249
|
having: Optional[List[Dict[str, Any]]] = None,
|
|
245
250
|
order_by: Optional[Dict[str, Any]] = None,
|
|
246
251
|
purpose: Optional[str] = None,
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
|
4
4
|
|
|
5
|
+
from vaultkit.utils.validation import TIME_BUCKETS
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
def discover_tool_def(
|
|
7
9
|
*,
|
|
@@ -110,6 +112,25 @@ def query_tool_def(
|
|
|
110
112
|
"required": ["logic", "conditions"],
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
time_bucket = {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": {
|
|
118
|
+
"field": {"type": "string", "description": "A timestamp or date field of the dataset."},
|
|
119
|
+
"bucket": {"type": "string", "enum": list(TIME_BUCKETS)},
|
|
120
|
+
"alias": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
|
|
123
|
+
"description": "Name of the result column (default <field>_<bucket>). Use it in order_by.",
|
|
124
|
+
},
|
|
125
|
+
"timezone": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"description": "IANA time zone the periods start in, e.g. 'America/Toronto'. Default UTC.",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
"required": ["field", "bucket"],
|
|
131
|
+
"additionalProperties": False,
|
|
132
|
+
}
|
|
133
|
+
|
|
113
134
|
return {
|
|
114
135
|
"name": "vaultkit_query",
|
|
115
136
|
"description": (
|
|
@@ -117,6 +138,8 @@ def query_tool_def(
|
|
|
117
138
|
"sensitive fields may be masked and some datasets require approval before data is returned. "
|
|
118
139
|
"Supports aggregation (count/sum/avg/min/max), grouping, having and sorting on a single dataset; "
|
|
119
140
|
"joins are not supported. "
|
|
141
|
+
"For a trend over time (per day, week, month, quarter or year), send ONE query that groups by a "
|
|
142
|
+
"time bucket of a timestamp field, not one query per period. "
|
|
120
143
|
"Returns data or a pending status if approval is required."
|
|
121
144
|
),
|
|
122
145
|
"input_schema": {
|
|
@@ -173,10 +196,17 @@ def query_tool_def(
|
|
|
173
196
|
},
|
|
174
197
|
"group_by": {
|
|
175
198
|
"type": "array",
|
|
176
|
-
"items": {"type": "string"},
|
|
199
|
+
"items": {"oneOf": [{"type": "string"}, time_bucket]},
|
|
177
200
|
"description": (
|
|
178
|
-
"Fields to group by
|
|
179
|
-
"every entry in 'fields'
|
|
201
|
+
"Fields to group by, or time buckets of a timestamp or date field. Optional: "
|
|
202
|
+
"defaults to 'fields'. If given, it must include every entry in 'fields'; a "
|
|
203
|
+
"bucketed field is not listed in 'fields'.\n\n"
|
|
204
|
+
"Example, failed payments per quarter in one query: "
|
|
205
|
+
"fields: [], aggregates: [{func: 'count', field: '*', alias: 'failures'}], "
|
|
206
|
+
"filters: [{field: 'status', operator: 'eq', value: 'failed'}], "
|
|
207
|
+
"group_by: [{field: 'created_at', bucket: 'quarter', alias: 'quarter'}], "
|
|
208
|
+
"order_by: {column: 'quarter'}. Each row has the quarter's start date and its count. "
|
|
209
|
+
"Mix with plain fields: group_by: ['status', {field: 'created_at', bucket: 'month'}]."
|
|
180
210
|
),
|
|
181
211
|
},
|
|
182
212
|
"having": {
|
|
@@ -203,7 +233,7 @@ def query_tool_def(
|
|
|
203
233
|
"properties": {
|
|
204
234
|
"column": {
|
|
205
235
|
"type": "string",
|
|
206
|
-
"description": "A field, an aggregate alias, or an aggregate expression.",
|
|
236
|
+
"description": "A field, an aggregate or time bucket alias, or an aggregate expression.",
|
|
207
237
|
},
|
|
208
238
|
"direction": {"type": "string", "enum": ["asc", "desc"], "default": "asc"},
|
|
209
239
|
},
|
|
@@ -131,19 +131,64 @@ def validate_aggregates(value: Optional[Any]) -> Optional[list]:
|
|
|
131
131
|
return value
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
# Units a group_by time bucket can use. The control plane checks the field is
|
|
135
|
+
# a timestamp or date column and that the time zone exists.
|
|
136
|
+
TIME_BUCKETS = ("day", "week", "month", "quarter", "year")
|
|
137
|
+
TIME_BUCKET_KEYS = {"field", "bucket", "alias", "timezone"}
|
|
138
|
+
|
|
139
|
+
|
|
134
140
|
def validate_group_by(value: Optional[Any]) -> Optional[list]:
|
|
135
141
|
if value is None:
|
|
136
142
|
return None
|
|
137
143
|
|
|
138
|
-
if not isinstance(value, list)
|
|
144
|
+
if not isinstance(value, list):
|
|
139
145
|
raise ValidationError(
|
|
140
|
-
"group_by must be a list of field names",
|
|
146
|
+
"group_by must be a list of field names or time buckets",
|
|
141
147
|
context=ErrorContext(raw=value),
|
|
142
148
|
)
|
|
143
149
|
|
|
150
|
+
for entry in value:
|
|
151
|
+
if isinstance(entry, dict):
|
|
152
|
+
_validate_time_bucket(entry)
|
|
153
|
+
elif not (isinstance(entry, str) and entry.strip()):
|
|
154
|
+
raise ValidationError(
|
|
155
|
+
"group_by must be a list of field names or time buckets",
|
|
156
|
+
context=ErrorContext(raw=value),
|
|
157
|
+
)
|
|
158
|
+
|
|
144
159
|
return value
|
|
145
160
|
|
|
146
161
|
|
|
162
|
+
def _validate_time_bucket(entry: Dict[str, Any]) -> None:
|
|
163
|
+
unknown = set(entry) - TIME_BUCKET_KEYS
|
|
164
|
+
if unknown:
|
|
165
|
+
raise ValidationError(
|
|
166
|
+
f"group_by time bucket has unknown keys: {', '.join(sorted(unknown))}",
|
|
167
|
+
context=ErrorContext(raw=entry),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
field = entry.get("field")
|
|
171
|
+
if not isinstance(field, str) or not field.strip():
|
|
172
|
+
raise ValidationError(
|
|
173
|
+
"group_by time bucket needs a 'field'",
|
|
174
|
+
context=ErrorContext(raw=entry),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
bucket = entry.get("bucket")
|
|
178
|
+
if not isinstance(bucket, str) or bucket.lower() not in TIME_BUCKETS:
|
|
179
|
+
raise ValidationError(
|
|
180
|
+
f"group_by time bucket must be one of {', '.join(TIME_BUCKETS)}",
|
|
181
|
+
context=ErrorContext(raw=entry),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
for key in ("alias", "timezone"):
|
|
185
|
+
if key in entry and not (isinstance(entry[key], str) and entry[key].strip()):
|
|
186
|
+
raise ValidationError(
|
|
187
|
+
f"group_by time bucket '{key}' must be a non-empty string",
|
|
188
|
+
context=ErrorContext(raw=entry),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
147
192
|
def validate_having(value: Optional[Any]) -> Optional[list]:
|
|
148
193
|
return _require_list_of_dicts("having", value)
|
|
149
194
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vaultkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: VaultKit Python SDK for policy-driven, runtime governed data access
|
|
5
5
|
Author-email: VaultKit <founders@vaultkit.io>
|
|
6
6
|
License: MIT
|
|
@@ -93,6 +93,24 @@ result = client.execute(
|
|
|
93
93
|
|
|
94
94
|
- `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
|
|
95
95
|
- `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
|
|
96
|
+
An entry can also be a time bucket of a timestamp or date field, which groups by period
|
|
97
|
+
(`day`, `week`, `month`, `quarter` or `year`; `alias` and `timezone` are optional, and
|
|
98
|
+
the time zone defaults to UTC). Use one bucketed query for a trend instead of one query
|
|
99
|
+
per period:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
client.execute(
|
|
103
|
+
dataset="payments",
|
|
104
|
+
aggregates=[{"func": "count", "field": "*", "alias": "failures"}],
|
|
105
|
+
filters=[{"field": "status", "operator": "eq", "value": "failed"}],
|
|
106
|
+
group_by=[{"field": "created_at", "bucket": "quarter", "alias": "quarter"}],
|
|
107
|
+
order_by={"column": "quarter"},
|
|
108
|
+
purpose="Failed payments per quarter",
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Bucketing a fully or partially masked field is denied, like grouping by it. Needs a
|
|
113
|
+
control plane with time buckets (vaultkit#106); older ones reject the request.
|
|
96
114
|
- `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
|
|
97
115
|
- `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
|
|
98
116
|
- `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|